mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-04-29 07:29:41 +00:00
Refactor UI to function elements & update React + Ant.
This refactor the UI components from class based element into function based elements. This makes it possible to use hooks that are used now by most React components. This also updates React and Ant to the latest versions (+ other dependencies).
This commit is contained in:
parent
afc196095d
commit
6f1638e87a
@ -12,3 +12,6 @@ dependencies:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
|
|
||||||
|
format:
|
||||||
|
./node_modules/.bin/prettier --write .
|
||||||
|
46
ui/README.md
46
ui/README.md
@ -1,46 +0,0 @@
|
|||||||
# Getting Started with Create React App
|
|
||||||
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
|
||||||
|
|
||||||
## Available Scripts
|
|
||||||
|
|
||||||
In the project directory, you can run:
|
|
||||||
|
|
||||||
### `yarn start`
|
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.\
|
|
||||||
You will also see any lint errors in the console.
|
|
||||||
|
|
||||||
### `yarn test`
|
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
||||||
|
|
||||||
### `yarn build`
|
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.\
|
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
||||||
|
|
||||||
### `yarn eject`
|
|
||||||
|
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
|
||||||
|
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
||||||
|
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
|
||||||
|
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
||||||
|
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
@ -3,76 +3,71 @@
|
|||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^6.0.0",
|
"@ant-design/colors": "^7.0.0",
|
||||||
|
"@ant-design/pro-layout": "^7.16.3",
|
||||||
"@chirpstack/chirpstack-api-grpc-web": "file:../api/grpc-web",
|
"@chirpstack/chirpstack-api-grpc-web": "file:../api/grpc-web",
|
||||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.18",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/jest": "^26.0.15",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/leaflet": "^1.7.5",
|
"@types/leaflet": "^1.9.3",
|
||||||
"@types/leaflet.awesome-markers": "^2.0.25",
|
"@types/leaflet.awesome-markers": "^2.0.25",
|
||||||
"@types/node": "^12.0.0",
|
"@types/node": "^16.18.38",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^18.2.15",
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"antd": "^5.7.1",
|
||||||
"antd": "^4.20.6",
|
|
||||||
"antd-mask-input": "^2.0.7",
|
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"chart.js": "^3.7.1",
|
"chart.js": "^4.3.0",
|
||||||
"chartjs-adapter-moment": "^1.0.0",
|
"chartjs-adapter-moment": "^1.0.1",
|
||||||
"chartjs-chart-matrix": "^1.1.1",
|
"chartjs-chart-matrix": "^2.0.1",
|
||||||
"codemirror": "^5.65.3",
|
"codemirror": "5.65.3",
|
||||||
"google-protobuf": "^3.21.2",
|
"history": "^5.3.0",
|
||||||
"grpc-web": "^1.4.2",
|
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"leaflet": "^1.7.1",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet.awesome-markers": "^2.0.5",
|
"leaflet.awesome-markers": "^2.0.5",
|
||||||
"react": "^17.0.2",
|
"moment": "^2.29.4",
|
||||||
"react-chartjs-2": "^4.1.0",
|
"react": "^18.2.0",
|
||||||
|
"react-chartjs-2": "^5.2.0",
|
||||||
"react-codemirror2": "^7.2.1",
|
"react-codemirror2": "^7.2.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.2.0",
|
||||||
"react-json-tree": "^0.15.1",
|
"react-json-tree": "0.15.1",
|
||||||
"react-leaflet": "^3.2.1",
|
"react-leaflet": "^4.2.1",
|
||||||
"react-markdown": "^8.0.3",
|
"react-markdown": "^8.0.7",
|
||||||
"react-router-dom": "^5.3.1",
|
"react-router-dom": "^6.14.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "^4.6.4",
|
"typescript": "^4.9.5",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject"
|
||||||
"lint": "prettier --check .",
|
|
||||||
"format": "prettier --write ."
|
|
||||||
},
|
|
||||||
"husky": {
|
|
||||||
"hooks": {
|
|
||||||
"pre-commit": "yarn format"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
"react-app",
|
"react-app",
|
||||||
"react-app/jest"
|
"react-app/jest"
|
||||||
],
|
|
||||||
"ignorePatterns": [
|
|
||||||
"**/*_pb.js"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
"not dead",
|
"not dead",
|
||||||
"not op_mini all"
|
"not op_mini all"
|
||||||
],
|
],
|
||||||
"proxy": "http://chirpstack:8080/",
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"proxy": "http://127.0.0.1:8080/",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"husky": "^7.0.4",
|
"prettier": "^3.0.0"
|
||||||
"prettier": "^2.6.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
109
ui/src/App.tsx
109
ui/src/App.tsx
@ -1,18 +1,22 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
import { Router, Route, Switch } from "react-router-dom";
|
import { Router, Routes, Route } from "react-router-dom";
|
||||||
|
|
||||||
import { Layout } from "antd";
|
import { Layout } from "antd";
|
||||||
|
|
||||||
import { User } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
|
import { User } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
|
||||||
|
|
||||||
import Menu from "./components/Menu";
|
|
||||||
import Header from "./components/Header";
|
import Header from "./components/Header";
|
||||||
|
import Menu from "./components/Menu";
|
||||||
|
|
||||||
// dashboard
|
// dashboard
|
||||||
import Dashboard from "./views/dashboard/Dashboard";
|
import Dashboard from "./views/dashboard/Dashboard";
|
||||||
|
|
||||||
// users
|
// users
|
||||||
import Login from "./views/users/Login";
|
import Login from "./views/users/Login";
|
||||||
|
import ListUsers from "./views/users/ListUsers";
|
||||||
|
import CreateUser from "./views/users/CreateUser";
|
||||||
|
import EditUser from "./views/users/EditUser";
|
||||||
|
import ChangeUserPassword from "./views/users/ChangeUserPassword";
|
||||||
|
|
||||||
// tenants
|
// tenants
|
||||||
import TenantRedirect from "./views/tenants/TenantRedirect";
|
import TenantRedirect from "./views/tenants/TenantRedirect";
|
||||||
@ -20,20 +24,14 @@ import ListTenants from "./views/tenants/ListTenants";
|
|||||||
import CreateTenant from "./views/tenants/CreateTenant";
|
import CreateTenant from "./views/tenants/CreateTenant";
|
||||||
import TenantLoader from "./views/tenants/TenantLoader";
|
import TenantLoader from "./views/tenants/TenantLoader";
|
||||||
|
|
||||||
// users
|
|
||||||
import ListUsers from "./views/users/ListUsers";
|
|
||||||
import CreateUser from "./views/users/CreateUser";
|
|
||||||
import EditUser from "./views/users/EditUser";
|
|
||||||
import ChangeUserPassword from "./views/users/ChangeUserPassword";
|
|
||||||
|
|
||||||
// api keys
|
// api keys
|
||||||
import ListAdminApiKeys from "./views/api-keys/ListAdminApiKeys";
|
import ListAdminApiKeys from "./views/api-keys/ListAdminApiKeys";
|
||||||
import CreateAdminApiKey from "./views/api-keys/CreateAdminApiKey";
|
import CreateAdminApiKey from "./views/api-keys/CreateAdminApiKey";
|
||||||
|
|
||||||
// device-profile templates
|
// device-profile templates
|
||||||
import ListDeviceProfileTemplates from "./views/device-profile-templates/ListDeviceProfileTemplates";
|
import ListDeviceProfileTemplates from "./views/device-profile-templates/ListDeviceProfileTemplates";
|
||||||
import EditDeviceProfileTemplate from "./views/device-profile-templates/EditDeviceProfileTemplate";
|
|
||||||
import CreateDeviceProfileTemplate from "./views/device-profile-templates/CreateDeviceProfileTemplate";
|
import CreateDeviceProfileTemplate from "./views/device-profile-templates/CreateDeviceProfileTemplate";
|
||||||
|
import EditDeviceProfileTemplate from "./views/device-profile-templates/EditDeviceProfileTemplate";
|
||||||
|
|
||||||
// regions
|
// regions
|
||||||
import ListRegions from "./views/regions/ListRegions";
|
import ListRegions from "./views/regions/ListRegions";
|
||||||
@ -44,85 +42,72 @@ import SessionStore from "./stores/SessionStore";
|
|||||||
|
|
||||||
import history from "./history";
|
import history from "./history";
|
||||||
|
|
||||||
interface IProps {}
|
const CustomRouter = ({ history, ...props }: any) => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
action: history.action,
|
||||||
|
location: history.location,
|
||||||
|
});
|
||||||
|
|
||||||
interface IState {
|
React.useLayoutEffect(() => history.listen(setState), [history]);
|
||||||
user?: User;
|
|
||||||
}
|
|
||||||
|
|
||||||
class App extends Component<IProps, IState> {
|
return <Router {...props} location={state.location} navigationType={state.action} navigator={history} />;
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
user: undefined,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
function App() {
|
||||||
|
const [user, setUser] = useState<User | undefined>(SessionStore.getUser());
|
||||||
SessionStore.on("change", () => {
|
SessionStore.on("change", () => {
|
||||||
this.setState({
|
setUser(SessionStore.getUser());
|
||||||
user: SessionStore.getUser(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
|
||||||
user: SessionStore.getUser(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Layout style={{ minHeight: "100vh" }}>
|
<Layout style={{ minHeight: "100vh" }}>
|
||||||
<Router history={history}>
|
<CustomRouter history={history}>
|
||||||
<Switch>
|
<Routes>
|
||||||
<Route exact path="/" component={TenantRedirect} />
|
<Route path="/" element={<TenantRedirect />} />
|
||||||
<Route exact path="/login" component={Login} />
|
<Route path="/login" element={<Login />} />
|
||||||
{this.state.user && (
|
</Routes>
|
||||||
<Route>
|
|
||||||
|
{user && (
|
||||||
|
<div>
|
||||||
<Layout.Header className="layout-header">
|
<Layout.Header className="layout-header">
|
||||||
<Header user={this.state.user} />
|
<Header user={user} />
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
<Layout className="layout">
|
<Layout className="layout">
|
||||||
<Layout.Sider width="300" theme="light" className="layout-menu">
|
<Layout.Sider width="300" theme="light" className="layout-menu">
|
||||||
<Menu />
|
<Menu />
|
||||||
</Layout.Sider>
|
</Layout.Sider>
|
||||||
<Layout.Content className="layout-content" style={{ padding: "24px 24px 24px" }}>
|
<Layout.Content className="layout-content" style={{ padding: "24px 24px 24px" }}>
|
||||||
<Switch>
|
<Routes>
|
||||||
<Route exact path="/dashboard" component={Dashboard} />
|
<Route path="/dashboard" element={<Dashboard />} />
|
||||||
|
<Route path="/tenants" element={<ListTenants />} />
|
||||||
|
<Route path="/tenants/create" element={<CreateTenant />} />
|
||||||
|
<Route path="/tenants/:tenantId/*" element={<TenantLoader />} />
|
||||||
|
|
||||||
<Route exact path="/tenants" component={ListTenants} />
|
<Route path="/users" element={<ListUsers />} />
|
||||||
<Route exact path="/tenants/create" component={CreateTenant} />
|
<Route path="/users/create" element={<CreateUser />} />
|
||||||
<Route path="/tenants/:tenantId([\w-]{36})" component={TenantLoader} />
|
<Route path="/users/:userId" element={<EditUser />} />
|
||||||
|
<Route path="/users/:userId/password" element={<ChangeUserPassword />} />
|
||||||
|
|
||||||
<Route exact path="/users" component={ListUsers} />
|
<Route path="/api-keys" element={<ListAdminApiKeys />} />
|
||||||
<Route exact path="/users/create" component={CreateUser} />
|
<Route path="/api-keys/create" element={<CreateAdminApiKey />} />
|
||||||
<Route exact path="/users/:userId([\w-]{36})" component={EditUser} />
|
|
||||||
<Route exact path="/users/:userId([\w-]{36})/password" component={ChangeUserPassword} />
|
|
||||||
|
|
||||||
<Route exact path="/api-keys" component={ListAdminApiKeys} />
|
<Route path="/device-profile-templates" element={<ListDeviceProfileTemplates />} />
|
||||||
<Route exact path="/api-keys/create" component={CreateAdminApiKey} />
|
<Route path="/device-profile-templates/create" element={<CreateDeviceProfileTemplate />} />
|
||||||
|
|
||||||
<Route exact path="/device-profile-templates" component={ListDeviceProfileTemplates} />
|
|
||||||
<Route exact path="/device-profile-templates/create" component={CreateDeviceProfileTemplate} />
|
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/device-profile-templates/:deviceProfileTemplateId/edit"
|
||||||
path="/device-profile-templates/:deviceProfileTemplateId([\w-]+)/edit"
|
element={<EditDeviceProfileTemplate />}
|
||||||
component={EditDeviceProfileTemplate}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route exact path="/regions" component={ListRegions} />
|
<Route path="/regions" element={<ListRegions />} />
|
||||||
<Route path="/regions/:id(.*)" component={RegionDetails} />
|
<Route path="/regions/:id" element={<RegionDetails />} />
|
||||||
</Switch>
|
</Routes>
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Route>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Switch>
|
</CustomRouter>
|
||||||
</Router>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component } from "react";
|
import React, { PropsWithChildren, useState, useEffect } from "react";
|
||||||
|
|
||||||
import SessionStore from "../stores/SessionStore";
|
import SessionStore from "../stores/SessionStore";
|
||||||
|
|
||||||
@ -9,73 +9,45 @@ interface IProps {
|
|||||||
isTenantAdmin?: boolean;
|
isTenantAdmin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function Admin(props: PropsWithChildren<IProps>) {
|
||||||
admin: boolean;
|
const [admin, setAdmin] = useState<boolean>(false);
|
||||||
}
|
|
||||||
|
|
||||||
class Admin extends Component<IProps, IState> {
|
const setIsAdmin = () => {
|
||||||
constructor(props: IProps) {
|
if (!props.isDeviceAdmin && !props.isGatewayAdmin && !props.isTenantAdmin) {
|
||||||
super(props);
|
setAdmin(SessionStore.isAdmin());
|
||||||
|
|
||||||
this.state = {
|
|
||||||
admin: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
SessionStore.on("change", this.setIsAdmin);
|
|
||||||
this.setIsAdmin();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
SessionStore.removeListener("change", this.setIsAdmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: IProps) {
|
|
||||||
if (prevProps === this.props) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setIsAdmin();
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsAdmin = () => {
|
|
||||||
if (!this.props.isDeviceAdmin && !this.props.isGatewayAdmin && !this.props.isTenantAdmin) {
|
|
||||||
this.setState({
|
|
||||||
admin: SessionStore.isAdmin(),
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
if (this.props.tenantId === undefined) {
|
if (props.tenantId === undefined) {
|
||||||
throw new Error("No tenantId is given");
|
throw new Error("No tenantId is given");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.isTenantAdmin) {
|
if (props.isTenantAdmin) {
|
||||||
this.setState({
|
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantAdmin(props.tenantId));
|
||||||
admin: SessionStore.isAdmin() || SessionStore.isTenantAdmin(this.props.tenantId),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.isDeviceAdmin) {
|
if (props.isDeviceAdmin) {
|
||||||
this.setState({
|
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantDeviceAdmin(props.tenantId));
|
||||||
admin: SessionStore.isAdmin() || SessionStore.isTenantDeviceAdmin(this.props.tenantId),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.isGatewayAdmin) {
|
if (props.isGatewayAdmin) {
|
||||||
this.setState({
|
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantGatewayAdmin(props.tenantId));
|
||||||
admin: SessionStore.isAdmin() || SessionStore.isTenantGatewayAdmin(this.props.tenantId),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
useEffect(() => {
|
||||||
if (this.state.admin) {
|
SessionStore.on("change", setIsAdmin);
|
||||||
return this.props.children;
|
setIsAdmin();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
SessionStore.removeListener("change", setIsAdmin);
|
||||||
|
};
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
|
if (admin) {
|
||||||
|
return <div>{props.children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default Admin;
|
export default Admin;
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { notification, Input, Select, Button, Space, Form, Dropdown, Menu } from "antd";
|
import { notification, Input, Select, Button, Space, Form, Dropdown, Menu } from "antd";
|
||||||
import { ReloadOutlined, CopyOutlined } from "@ant-design/icons";
|
import { ReloadOutlined, CopyOutlined } from "@ant-design/icons";
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
formRef: React.RefObject<any>;
|
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
@ -14,42 +13,29 @@ interface IProps {
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function AesKeyInput(props: IProps) {
|
||||||
byteOrder: string;
|
const form = Form.useFormInstance();
|
||||||
value: string;
|
const [byteOrder, setByteOrder] = useState<string>("msb");
|
||||||
|
const [value, setValue] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.value) {
|
||||||
|
setValue(props.value);
|
||||||
|
}
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
|
const updateField = (v: string) => {
|
||||||
|
if (byteOrder === "lsb") {
|
||||||
|
const bytes = v.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||||
|
v = bytes.reverse().join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
class AesKeyInput extends Component<IProps, IState> {
|
form.setFieldsValue({
|
||||||
constructor(props: IProps) {
|
[props.name]: v,
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
byteOrder: "msb",
|
|
||||||
value: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
updateField = () => {
|
|
||||||
let value = this.state.value;
|
|
||||||
|
|
||||||
if (this.state.byteOrder === "lsb") {
|
|
||||||
const bytes = value.match(/[A-Fa-f0-9]{2}/g) || [];
|
|
||||||
value = bytes.reverse().join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.formRef.current.setFieldsValue({
|
|
||||||
[this.props.name]: value,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (this.props.value) {
|
|
||||||
this.setState({
|
|
||||||
value: this.props.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
let v = e.target.value;
|
let v = e.target.value;
|
||||||
const match = v.match(/[A-Fa-f0-9]/g);
|
const match = v.match(/[A-Fa-f0-9]/g);
|
||||||
|
|
||||||
@ -62,50 +48,37 @@ class AesKeyInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(
|
setValue(value);
|
||||||
{
|
updateField(value);
|
||||||
value: value,
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onByteOrderSelect = (v: string) => {
|
const onByteOrderSelect = (v: string) => {
|
||||||
if (v === this.state.byteOrder) {
|
if (v === byteOrder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setByteOrder(v);
|
||||||
byteOrder: v,
|
|
||||||
});
|
|
||||||
|
|
||||||
const current = this.state.value;
|
const current = value;
|
||||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||||
|
const vv = bytes.reverse().join("");
|
||||||
|
|
||||||
this.setState(
|
setValue(vv);
|
||||||
{
|
updateField(vv);
|
||||||
value: bytes.reverse().join(""),
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
generateRandom = () => {
|
const generateRandom = () => {
|
||||||
let cryptoObj = window.crypto || window.Crypto;
|
let cryptoObj = window.crypto || window.Crypto;
|
||||||
let b = new Uint8Array(16);
|
let b = new Uint8Array(16);
|
||||||
cryptoObj.getRandomValues(b);
|
cryptoObj.getRandomValues(b);
|
||||||
|
|
||||||
let key = Buffer.from(b).toString("hex");
|
let key = Buffer.from(b).toString("hex");
|
||||||
this.setState(
|
setValue(key);
|
||||||
{
|
updateField(key);
|
||||||
value: key,
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
copyToClipboard = () => {
|
const copyToClipboard = () => {
|
||||||
const bytes = this.state.value.match(/[A-Fa-f0-9]{2}/g);
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
||||||
|
|
||||||
if (bytes !== null && navigator.clipboard !== undefined) {
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
@ -126,8 +99,8 @@ class AesKeyInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
copyToClipboardHexArray = () => {
|
const copyToClipboardHexArray = () => {
|
||||||
const bytes = this.state.value.match(/[A-Fa-f0-9]{2}/g);
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
||||||
|
|
||||||
if (bytes !== null && navigator.clipboard !== undefined) {
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
@ -153,14 +126,13 @@ class AesKeyInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const copyMenu = (
|
const copyMenu = (
|
||||||
<Menu
|
<Menu
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: "1",
|
key: "1",
|
||||||
label: (
|
label: (
|
||||||
<Button type="text" onClick={this.copyToClipboard}>
|
<Button type="text" onClick={copyToClipboard}>
|
||||||
HEX string
|
HEX string
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -168,7 +140,7 @@ class AesKeyInput extends Component<IProps, IState> {
|
|||||||
{
|
{
|
||||||
key: "2",
|
key: "2",
|
||||||
label: (
|
label: (
|
||||||
<Button type="text" onClick={this.copyToClipboardHexArray}>
|
<Button type="text" onClick={copyToClipboardHexArray}>
|
||||||
HEX array
|
HEX array
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -179,11 +151,11 @@ class AesKeyInput extends Component<IProps, IState> {
|
|||||||
|
|
||||||
const addon = (
|
const addon = (
|
||||||
<Space size="large">
|
<Space size="large">
|
||||||
<Select value={this.state.byteOrder} onChange={this.onByteOrderSelect}>
|
<Select value={byteOrder} onChange={onByteOrderSelect}>
|
||||||
<Select.Option value="msb">MSB</Select.Option>
|
<Select.Option value="msb">MSB</Select.Option>
|
||||||
<Select.Option value="lsb">LSB</Select.Option>
|
<Select.Option value="lsb">LSB</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
<Button type="text" size="small" onClick={generateRandom}>
|
||||||
<ReloadOutlined />
|
<ReloadOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
<Dropdown overlay={copyMenu}>
|
<Dropdown overlay={copyMenu}>
|
||||||
@ -198,27 +170,26 @@ class AesKeyInput extends Component<IProps, IState> {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: this.props.required,
|
required: props.required,
|
||||||
message: `Please enter a valid ${this.props.label}`,
|
message: `Please enter a valid ${props.label}`,
|
||||||
pattern: new RegExp(/[A-Fa-f0-9]{32}/g),
|
pattern: new RegExp(/[A-Fa-f0-9]{32}/g),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
label={this.props.label}
|
label={props.label}
|
||||||
name={this.props.name}
|
name={props.name}
|
||||||
tooltip={this.props.tooltip}
|
tooltip={props.tooltip}
|
||||||
>
|
>
|
||||||
<Input hidden />
|
<Input hidden />
|
||||||
<Input.Password
|
<Input
|
||||||
id={`${this.props.name}Render`}
|
id={`${props.name}Render`}
|
||||||
onChange={this.onChange}
|
onChange={onChange}
|
||||||
addonAfter={!this.props.disabled && addon}
|
addonAfter={!props.disabled && addon}
|
||||||
style={{ fontFamily: "monospace" }}
|
className="input-code"
|
||||||
value={this.state.value}
|
value={value}
|
||||||
disabled={this.props.disabled}
|
disabled={props.disabled}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default AesKeyInput;
|
export default AesKeyInput;
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { Select } from "antd";
|
import { Select } from "antd";
|
||||||
|
|
||||||
export type OptionsCallbackFunc = (o: { label: string; value: string }[]) => void;
|
export type OptionsCallbackFunc = (o: { label: string; value: string }[]) => void;
|
||||||
export type OptionCallbackFunc = (o: { label: string; value: string }) => void;
|
export type OptionCallbackFunc = (o: { label: string; value: string }) => void;
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
className: string;
|
className: string;
|
||||||
@ -14,93 +19,59 @@ interface IProps {
|
|||||||
onSelect?: (s: string) => void;
|
onSelect?: (s: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function AutoComplete({ placeholder, className, value, getOption, getOptions, onSelect }: IProps) {
|
||||||
option?: { label: string; value: string };
|
const [option, setOption] = useState<Option | undefined>(undefined);
|
||||||
options: { label: string; value: string }[];
|
const [options, setOptions] = useState<Option[]>([]);
|
||||||
}
|
|
||||||
|
|
||||||
class Autocomplete extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
if (value && value !== "") {
|
||||||
super(props);
|
getOption(value, (o: Option) => {
|
||||||
|
setOptions([o]);
|
||||||
this.state = {
|
|
||||||
options: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (this.props.value && this.props.value !== "") {
|
|
||||||
this.props.getOption(this.props.value, (o: { label: string; value: string }) => {
|
|
||||||
this.setState({
|
|
||||||
options: [o],
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}, [value, getOption]);
|
||||||
|
|
||||||
componentDidUpdate(prevProps: IProps) {
|
const onFocus = () => {
|
||||||
if (this.props.value === prevProps.value) {
|
getOptions("", options => {
|
||||||
return;
|
if (option !== undefined) {
|
||||||
}
|
const selected = option.value;
|
||||||
|
|
||||||
if (this.props.value && this.props.value !== "") {
|
|
||||||
this.props.getOption(this.props.value, (o: { label: string; value: string }) => {
|
|
||||||
this.setState({
|
|
||||||
options: [o],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFocus = () => {
|
|
||||||
this.props.getOptions("", options => {
|
|
||||||
if (this.state.option !== undefined) {
|
|
||||||
const selected = this.state.option.value;
|
|
||||||
|
|
||||||
if (options.find(e => e.value === selected) === undefined) {
|
if (options.find(e => e.value === selected) === undefined) {
|
||||||
options.unshift(this.state.option);
|
options.unshift(option);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setOptions(options);
|
||||||
options: options,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onSearch = (value: string) => {
|
const onSearch = (value: string) => {
|
||||||
this.props.getOptions(value, options => {
|
getOptions(value, options => {
|
||||||
this.setState({
|
setOptions(options);
|
||||||
options: options,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onSelect = (value: string, option: any) => {
|
const onSelectFn = (value: string, option: any) => {
|
||||||
this.setState({
|
setOption({ label: option.label, value: option.value });
|
||||||
option: { label: option.label, value: option.value },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.props.onSelect !== undefined) {
|
if (onSelect !== undefined) {
|
||||||
this.props.onSelect(value);
|
onSelect(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const { getOption, getOptions, ...otherProps } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
showSearch
|
showSearch
|
||||||
options={this.state.options}
|
options={options}
|
||||||
onFocus={this.onFocus}
|
onFocus={onFocus}
|
||||||
onSearch={this.onSearch}
|
onSearch={onSearch}
|
||||||
onSelect={this.onSelect}
|
onSelect={onSelectFn}
|
||||||
filterOption={false}
|
filterOption={false}
|
||||||
{...otherProps}
|
placeholder={placeholder}
|
||||||
|
className={className}
|
||||||
|
value={value}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default Autocomplete;
|
export default AutoComplete;
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form } from "antd";
|
import { Form } from "antd";
|
||||||
|
|
||||||
import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "./Autocomplete";
|
import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "./Autocomplete";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
formRef: React.RefObject<any>;
|
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
@ -14,28 +11,35 @@ interface IProps {
|
|||||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void;
|
getOptions: (s: string, fn: OptionsCallbackFunc) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutocompleteInput extends Component<IProps> {
|
function AutocompleteInput(props: IProps) {
|
||||||
render() {
|
const form = Form.useFormInstance();
|
||||||
|
|
||||||
|
const onSelect = (value: string) => {
|
||||||
|
form.setFieldsValue({
|
||||||
|
[props.name]: value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: this.props.required,
|
required: props.required,
|
||||||
message: `Please select a ${this.props.label}`,
|
message: `Please select a ${props.label}`,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
label={this.props.label}
|
label={props.label}
|
||||||
name={this.props.name}
|
name={props.name}
|
||||||
>
|
>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
placeholder={`Select a ${this.props.label}`}
|
placeholder={`Select a ${props.label}`}
|
||||||
className=""
|
className=""
|
||||||
getOption={this.props.getOption}
|
getOption={props.getOption}
|
||||||
getOptions={this.props.getOptions}
|
getOptions={props.getOptions}
|
||||||
|
onSelect={onSelect}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default AutocompleteInput;
|
export default AutocompleteInput;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Controlled as CodeMirror } from "react-codemirror2";
|
import { Controlled as CodeMirror } from "react-codemirror2";
|
||||||
|
|
||||||
import { Form } from "antd";
|
import { Form } from "antd";
|
||||||
@ -6,88 +6,49 @@ import { Form } from "antd";
|
|||||||
import "codemirror/mode/javascript/javascript";
|
import "codemirror/mode/javascript/javascript";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
formRef: React.RefObject<any>;
|
|
||||||
label?: string;
|
label?: string;
|
||||||
name: string;
|
name: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
value?: string;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function CodeEditor(props: IProps) {
|
||||||
value: string;
|
const form = Form.useFormInstance();
|
||||||
reloadKey: number;
|
const [value, setValue] = useState<string>("");
|
||||||
}
|
const [reloadKey, setReloadKey] = useState<number>(1);
|
||||||
|
|
||||||
class CodeEditor extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
setValue(form.getFieldValue(props.name));
|
||||||
super(props);
|
setReloadKey(k => k + 1);
|
||||||
this.state = {
|
}, [form, props]);
|
||||||
value: "",
|
|
||||||
reloadKey: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
const handleChange = (editor: any, data: any, newCode: string) => {
|
||||||
if (this.props.value) {
|
setValue(newCode);
|
||||||
this.setState({
|
form.setFieldsValue({
|
||||||
value: this.props.value,
|
[props.name]: newCode,
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(oldProps: IProps) {
|
|
||||||
if (this.props === oldProps) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.value) {
|
|
||||||
this.setState({
|
|
||||||
value: this.props.value,
|
|
||||||
reloadKey: this.state.reloadKey + 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateField = () => {
|
|
||||||
let value = this.state.value;
|
|
||||||
|
|
||||||
this.props.formRef.current.setFieldsValue({
|
|
||||||
[this.props.name]: value,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChange = (editor: any, data: any, newCode: string) => {
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
value: newCode,
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const codeMirrorOptions = {
|
const codeMirrorOptions = {
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
mode: "javascript",
|
mode: "javascript",
|
||||||
theme: "base16-light",
|
theme: "base16-light",
|
||||||
readOnly: this.props.disabled,
|
readOnly: props.disabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item label={this.props.label} name={this.props.name} tooltip={this.props.tooltip}>
|
<Form.Item label={props.label} name={props.name} tooltip={props.tooltip}>
|
||||||
<div style={{ border: "1px solid #cccccc" }}>
|
<div style={{ border: "1px solid #cccccc" }}>
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
key={`code-editor-refresh-${this.state.reloadKey}`}
|
key={`code-editor-refresh-${reloadKey}`}
|
||||||
value={this.state.value}
|
value={value}
|
||||||
options={codeMirrorOptions}
|
options={codeMirrorOptions}
|
||||||
onBeforeChange={this.handleChange}
|
onBeforeChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CodeEditor;
|
export default CodeEditor;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { Table } from "antd";
|
import { Table } from "antd";
|
||||||
import { ColumnsType } from "antd/es/table";
|
import { ColumnsType } from "antd/es/table";
|
||||||
@ -16,113 +16,81 @@ interface IProps {
|
|||||||
noPagination?: boolean;
|
noPagination?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DataTable(props: IProps) {
|
||||||
totalCount: number;
|
const [totalCount, setTotalCount] = useState<number>(0);
|
||||||
pageSize: number;
|
const [pageSize, setPageSize] = useState<number>(SessionStore.getRowsPerPage());
|
||||||
currentPage: number;
|
const [currentPage, setCurrentPage] = useState<number>(1);
|
||||||
rows: object[];
|
const [rows, setRows] = useState<object[]>([]);
|
||||||
loading: boolean;
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
}
|
|
||||||
|
|
||||||
class DataTable extends Component<IProps, IState> {
|
const onChangePage = (page: number, pz?: number | void) => {
|
||||||
constructor(props: IProps) {
|
setLoading(true);
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
totalCount: 0,
|
|
||||||
pageSize: SessionStore.getRowsPerPage(),
|
|
||||||
currentPage: 1,
|
|
||||||
rows: [],
|
|
||||||
loading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.onChangePage(this.state.currentPage, this.state.pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: IProps) {
|
|
||||||
if (this.props === prevProps) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onChangePage(this.state.currentPage, this.state.pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangePage = (page: number, pageSize?: number | void) => {
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
loading: true,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
let pz = pageSize;
|
|
||||||
if (!pz) {
|
if (!pz) {
|
||||||
pz = this.state.pageSize;
|
pz = pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => {
|
props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => {
|
||||||
this.setState({
|
setCurrentPage(page);
|
||||||
currentPage: page,
|
setTotalCount(totalCount);
|
||||||
totalCount: totalCount,
|
setRows(rows);
|
||||||
rows: rows,
|
setPageSize(pz || 0);
|
||||||
pageSize: pz || 0,
|
setLoading(false);
|
||||||
loading: false,
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onShowSizeChange = (page: number, pageSize: number) => {
|
const onShowSizeChange = (page: number, pageSize: number) => {
|
||||||
this.onChangePage(page, pageSize);
|
onChangePage(page, pageSize);
|
||||||
SessionStore.setRowsPerPage(pageSize);
|
SessionStore.setRowsPerPage(pageSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
onRowsSelectChange = (ids: React.Key[]) => {
|
const onRowsSelectChange = (ids: React.Key[]) => {
|
||||||
const idss = ids as string[];
|
const idss = ids as string[];
|
||||||
if (this.props.onRowsSelectChange) {
|
if (props.onRowsSelectChange) {
|
||||||
this.props.onRowsSelectChange(idss);
|
props.onRowsSelectChange(idss);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
useEffect(() => {
|
||||||
const { getPage, refreshKey, ...otherProps } = this.props;
|
onChangePage(currentPage, pageSize);
|
||||||
|
}, [props, currentPage, pageSize]);
|
||||||
|
|
||||||
|
const { getPage, refreshKey, ...otherProps } = props;
|
||||||
let loadingProps = undefined;
|
let loadingProps = undefined;
|
||||||
if (this.state.loading) {
|
if (loading) {
|
||||||
loadingProps = {
|
loadingProps = {
|
||||||
delay: 300,
|
delay: 300,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let pagination = undefined;
|
let pagination = undefined;
|
||||||
if (this.props.noPagination === undefined || this.props.noPagination === false) {
|
if (props.noPagination === undefined || props.noPagination === false) {
|
||||||
pagination = {
|
pagination = {
|
||||||
current: this.state.currentPage,
|
current: currentPage,
|
||||||
total: this.state.totalCount,
|
total: totalCount,
|
||||||
pageSize: this.state.pageSize,
|
pageSize: pageSize,
|
||||||
onChange: this.onChangePage,
|
onChange: onChangePage,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
onShowSizeChange: this.onShowSizeChange,
|
onShowSizeChange: onShowSizeChange,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let rowSelection = undefined;
|
let rowSelection = undefined;
|
||||||
if (this.props.onRowsSelectChange) {
|
if (props.onRowsSelectChange) {
|
||||||
rowSelection = {
|
rowSelection = {
|
||||||
onChange: this.onRowsSelectChange,
|
onChange: onRowsSelectChange,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
loading={loadingProps}
|
loading={loadingProps}
|
||||||
dataSource={this.state.rows}
|
dataSource={rows}
|
||||||
pagination={pagination || false}
|
pagination={pagination || false}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DataTable;
|
export default DataTable;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useState, PropsWithChildren } from "react";
|
||||||
|
|
||||||
import { Popover, Button, Typography, Space, Input } from "antd";
|
import { Popover, Button, Typography, Space, Input } from "antd";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -8,51 +7,32 @@ interface IProps {
|
|||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConfirmState {
|
function DeleteConfirmContent(props: IProps) {
|
||||||
confirm: string;
|
const [confirm, setConfirm] = useState<string>("");
|
||||||
}
|
|
||||||
|
|
||||||
class DeleteConfirmContent extends Component<IProps, ConfirmState> {
|
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
constructor(props: IProps) {
|
setConfirm(e.target.value);
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
confirm: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.setState({
|
|
||||||
confirm: e.target.value,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
Enter '{this.props.confirm}' to confirm you want to delete this {this.props.typ}:
|
Enter '{props.confirm}' to confirm you want to delete this {props.typ}:
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Input placeholder={this.props.confirm} onChange={this.onChange} />
|
<Input placeholder={props.confirm} onChange={onChange} />
|
||||||
<Button
|
<Button onClick={props.onConfirm} disabled={confirm !== props.confirm} style={{ float: "right" }}>
|
||||||
onClick={this.props.onConfirm}
|
|
||||||
disabled={this.state.confirm !== this.props.confirm}
|
|
||||||
style={{ float: "right" }}
|
|
||||||
>
|
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class DeleteConfirm extends Component<IProps> {
|
function DeleteConfirm(props: PropsWithChildren<IProps>) {
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Popover content={<DeleteConfirmContent {...this.props} />} trigger="click" placement="left">
|
<Popover content={<DeleteConfirmContent {...props} />} trigger="click" placement="left">
|
||||||
{this.props.children}
|
{props.children}
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DeleteConfirm;
|
export default DeleteConfirm;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { notification, Input, Select, Button, Space, Form, Dropdown, Menu } from "antd";
|
import { notification, Input, Select, Button, Space, Form, Dropdown, Menu } from "antd";
|
||||||
import { ReloadOutlined, CopyOutlined } from "@ant-design/icons";
|
import { ReloadOutlined, CopyOutlined } from "@ant-design/icons";
|
||||||
@ -8,7 +8,6 @@ import { GetRandomDevAddrRequest, GetRandomDevAddrResponse } from "@chirpstack/c
|
|||||||
import DeviceStore from "../stores/DeviceStore";
|
import DeviceStore from "../stores/DeviceStore";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
formRef: React.RefObject<any>;
|
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
devEui: string;
|
devEui: string;
|
||||||
@ -17,42 +16,29 @@ interface IProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DevAddrInput(props: IProps) {
|
||||||
byteOrder: string;
|
const form = Form.useFormInstance();
|
||||||
value: string;
|
const [byteOrder, setByteOrder] = useState<string>("msb");
|
||||||
|
const [value, setValue] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.value) {
|
||||||
|
setValue(props.value);
|
||||||
|
}
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
|
const updateField = (v: string) => {
|
||||||
|
if (byteOrder === "lsb") {
|
||||||
|
const bytes = v.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||||
|
v = bytes.reverse().join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
class DevAddrInput extends Component<IProps, IState> {
|
form.setFieldsValue({
|
||||||
constructor(props: IProps) {
|
[props.name]: v,
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
byteOrder: "msb",
|
|
||||||
value: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
updateField = () => {
|
|
||||||
let value = this.state.value;
|
|
||||||
|
|
||||||
if (this.state.byteOrder === "lsb") {
|
|
||||||
const bytes = value.match(/[A-Fa-f0-9]{2}/g) || [];
|
|
||||||
value = bytes.reverse().join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.formRef.current.setFieldsValue({
|
|
||||||
[this.props.name]: value,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (this.props.value) {
|
|
||||||
this.setState({
|
|
||||||
value: this.props.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
let v = e.target.value;
|
let v = e.target.value;
|
||||||
const match = v.match(/[A-Fa-f0-9]/g);
|
const match = v.match(/[A-Fa-f0-9]/g);
|
||||||
|
|
||||||
@ -65,50 +51,37 @@ class DevAddrInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(
|
setValue(value);
|
||||||
{
|
updateField(value);
|
||||||
value: value,
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onByteOrderSelect = (v: string) => {
|
const onByteOrderSelect = (v: string) => {
|
||||||
if (v === this.state.byteOrder) {
|
if (v === byteOrder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setByteOrder(v);
|
||||||
byteOrder: v,
|
|
||||||
});
|
|
||||||
|
|
||||||
const current = this.state.value;
|
const current = value;
|
||||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||||
|
const vv = bytes.reverse().join("");
|
||||||
|
|
||||||
this.setState(
|
setValue(vv);
|
||||||
{
|
updateField(vv);
|
||||||
value: bytes.reverse().join(""),
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
generateRandom = () => {
|
const generateRandom = () => {
|
||||||
let req = new GetRandomDevAddrRequest();
|
let req = new GetRandomDevAddrRequest();
|
||||||
req.setDevEui(this.props.devEui);
|
req.setDevEui(props.devEui);
|
||||||
|
|
||||||
DeviceStore.getRandomDevAddr(req, (resp: GetRandomDevAddrResponse) => {
|
DeviceStore.getRandomDevAddr(req, (resp: GetRandomDevAddrResponse) => {
|
||||||
this.setState(
|
setValue(resp.getDevAddr());
|
||||||
{
|
updateField(resp.getDevAddr());
|
||||||
value: resp.getDevAddr(),
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
copyToClipboard = () => {
|
const copyToClipboard = () => {
|
||||||
const bytes = this.state.value.match(/[A-Fa-f0-9]{2}/g);
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
||||||
|
|
||||||
if (bytes !== null && navigator.clipboard !== undefined) {
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
@ -129,8 +102,8 @@ class DevAddrInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
copyToClipboardHexArray = () => {
|
const copyToClipboardHexArray = () => {
|
||||||
const bytes = this.state.value.match(/[A-Fa-f0-9]{2}/g);
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
||||||
|
|
||||||
if (bytes !== null && navigator.clipboard !== undefined) {
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
@ -156,14 +129,13 @@ class DevAddrInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const copyMenu = (
|
const copyMenu = (
|
||||||
<Menu
|
<Menu
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: "1",
|
key: "1",
|
||||||
label: (
|
label: (
|
||||||
<Button type="text" onClick={this.copyToClipboard}>
|
<Button type="text" onClick={copyToClipboard}>
|
||||||
HEX string
|
HEX string
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -171,7 +143,7 @@ class DevAddrInput extends Component<IProps, IState> {
|
|||||||
{
|
{
|
||||||
key: "2",
|
key: "2",
|
||||||
label: (
|
label: (
|
||||||
<Button type="text" onClick={this.copyToClipboardHexArray}>
|
<Button type="text" onClick={copyToClipboardHexArray}>
|
||||||
HEX array
|
HEX array
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -182,11 +154,11 @@ class DevAddrInput extends Component<IProps, IState> {
|
|||||||
|
|
||||||
const addon = (
|
const addon = (
|
||||||
<Space size="large">
|
<Space size="large">
|
||||||
<Select value={this.state.byteOrder} onChange={this.onByteOrderSelect}>
|
<Select value={byteOrder} onChange={onByteOrderSelect}>
|
||||||
<Select.Option value="msb">MSB</Select.Option>
|
<Select.Option value="msb">MSB</Select.Option>
|
||||||
<Select.Option value="lsb">LSB</Select.Option>
|
<Select.Option value="lsb">LSB</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
<Button type="text" size="small" onClick={generateRandom}>
|
||||||
<ReloadOutlined />
|
<ReloadOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
<Dropdown overlay={copyMenu}>
|
<Dropdown overlay={copyMenu}>
|
||||||
@ -201,26 +173,25 @@ class DevAddrInput extends Component<IProps, IState> {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: this.props.required,
|
required: props.required,
|
||||||
message: `Please enter a valid ${this.props.label}`,
|
message: `Please enter a valid ${props.label}`,
|
||||||
pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
|
pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
label={this.props.label}
|
label={props.label}
|
||||||
name={this.props.name}
|
name={props.name}
|
||||||
>
|
>
|
||||||
<Input hidden />
|
<Input hidden />
|
||||||
<Input
|
<Input
|
||||||
id={`${this.props.name}Render`}
|
id={`${props.name}Render`}
|
||||||
onChange={this.onChange}
|
onChange={onChange}
|
||||||
addonAfter={!this.props.disabled && addon}
|
addonAfter={!props.disabled && addon}
|
||||||
style={{ fontFamily: "monospace" }}
|
className="input-code"
|
||||||
value={this.state.value}
|
value={value}
|
||||||
disabled={this.props.disabled}
|
disabled={props.disabled}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DevAddrInput;
|
export default DevAddrInput;
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { notification, Input, Select, Button, Space, Form, Dropdown, Menu } from "antd";
|
import { notification, Input, Select, Button, Space, Form, Dropdown, Menu } from "antd";
|
||||||
import { ReloadOutlined, CopyOutlined } from "@ant-design/icons";
|
import { ReloadOutlined, CopyOutlined } from "@ant-design/icons";
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
formRef: React.RefObject<any>;
|
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
@ -14,42 +13,29 @@ interface IProps {
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EuiInput(props: IProps) {
|
||||||
byteOrder: string;
|
const form = Form.useFormInstance();
|
||||||
value: string;
|
const [byteOrder, setByteOrder] = useState<string>("msb");
|
||||||
|
const [value, setValue] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.value) {
|
||||||
|
setValue(props.value);
|
||||||
|
}
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
|
const updateField = (v: string) => {
|
||||||
|
if (byteOrder === "lsb") {
|
||||||
|
const bytes = v.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||||
|
v = bytes.reverse().join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
class EuiInput extends Component<IProps, IState> {
|
form.setFieldsValue({
|
||||||
constructor(props: IProps) {
|
[props.name]: v,
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
byteOrder: "msb",
|
|
||||||
value: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
updateField = () => {
|
|
||||||
let value = this.state.value;
|
|
||||||
|
|
||||||
if (this.state.byteOrder === "lsb") {
|
|
||||||
const bytes = value.match(/[A-Fa-f0-9]{2}/g) || [];
|
|
||||||
value = bytes.reverse().join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.formRef.current.setFieldsValue({
|
|
||||||
[this.props.name]: value,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (this.props.value) {
|
|
||||||
this.setState({
|
|
||||||
value: this.props.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
let v = e.target.value;
|
let v = e.target.value;
|
||||||
const match = v.match(/[A-Fa-f0-9]/g);
|
const match = v.match(/[A-Fa-f0-9]/g);
|
||||||
|
|
||||||
@ -62,50 +48,37 @@ class EuiInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(
|
setValue(value);
|
||||||
{
|
updateField(value);
|
||||||
value: value,
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onByteOrderSelect = (v: string) => {
|
const onByteOrderSelect = (v: string) => {
|
||||||
if (v === this.state.byteOrder) {
|
if (v === byteOrder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setByteOrder(v);
|
||||||
byteOrder: v,
|
|
||||||
});
|
|
||||||
|
|
||||||
const current = this.state.value;
|
const current = value;
|
||||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||||
|
const vv = bytes.reverse().join("");
|
||||||
|
|
||||||
this.setState(
|
setValue(vv);
|
||||||
{
|
updateField(vv);
|
||||||
value: bytes.reverse().join(""),
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
generateRandom = () => {
|
const generateRandom = () => {
|
||||||
let cryptoObj = window.crypto || window.Crypto;
|
let cryptoObj = window.crypto || window.Crypto;
|
||||||
let b = new Uint8Array(8);
|
let b = new Uint8Array(8);
|
||||||
cryptoObj.getRandomValues(b);
|
cryptoObj.getRandomValues(b);
|
||||||
|
|
||||||
let key = Buffer.from(b).toString("hex");
|
let key = Buffer.from(b).toString("hex");
|
||||||
this.setState(
|
setValue(key);
|
||||||
{
|
updateField(key);
|
||||||
value: key,
|
|
||||||
},
|
|
||||||
this.updateField,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
copyToClipboard = () => {
|
const copyToClipboard = () => {
|
||||||
const bytes = this.state.value.match(/[A-Fa-f0-9]{2}/g);
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
||||||
|
|
||||||
if (bytes !== null && navigator.clipboard !== undefined) {
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
@ -126,8 +99,8 @@ class EuiInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
copyToClipboardHexArray = () => {
|
const copyToClipboardHexArray = () => {
|
||||||
const bytes = this.state.value.match(/[A-Fa-f0-9]{2}/g);
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
||||||
|
|
||||||
if (bytes !== null && navigator.clipboard !== undefined) {
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
@ -153,14 +126,13 @@ class EuiInput extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const copyMenu = (
|
const copyMenu = (
|
||||||
<Menu
|
<Menu
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: "1",
|
key: "1",
|
||||||
label: (
|
label: (
|
||||||
<Button type="text" onClick={this.copyToClipboard}>
|
<Button type="text" onClick={copyToClipboard}>
|
||||||
HEX string
|
HEX string
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -168,7 +140,7 @@ class EuiInput extends Component<IProps, IState> {
|
|||||||
{
|
{
|
||||||
key: "2",
|
key: "2",
|
||||||
label: (
|
label: (
|
||||||
<Button type="text" onClick={this.copyToClipboardHexArray}>
|
<Button type="text" onClick={copyToClipboardHexArray}>
|
||||||
HEX array
|
HEX array
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@ -179,11 +151,11 @@ class EuiInput extends Component<IProps, IState> {
|
|||||||
|
|
||||||
const addon = (
|
const addon = (
|
||||||
<Space size="large">
|
<Space size="large">
|
||||||
<Select value={this.state.byteOrder} onChange={this.onByteOrderSelect}>
|
<Select value={byteOrder} onChange={onByteOrderSelect}>
|
||||||
<Select.Option value="msb">MSB</Select.Option>
|
<Select.Option value="msb">MSB</Select.Option>
|
||||||
<Select.Option value="lsb">LSB</Select.Option>
|
<Select.Option value="lsb">LSB</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
<Button type="text" size="small" onClick={this.generateRandom}>
|
<Button type="text" size="small" onClick={generateRandom}>
|
||||||
<ReloadOutlined />
|
<ReloadOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
<Dropdown overlay={copyMenu}>
|
<Dropdown overlay={copyMenu}>
|
||||||
@ -198,27 +170,26 @@ class EuiInput extends Component<IProps, IState> {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: this.props.required,
|
required: props.required,
|
||||||
message: `Please enter a valid ${this.props.label}`,
|
message: `Please enter a valid ${props.label}`,
|
||||||
pattern: new RegExp(/[A-Fa-f0-9]{16}/g),
|
pattern: new RegExp(/[A-Fa-f0-9]{16}/g),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
label={this.props.label}
|
label={props.label}
|
||||||
name={this.props.name}
|
name={props.name}
|
||||||
tooltip={this.props.tooltip}
|
tooltip={props.tooltip}
|
||||||
>
|
>
|
||||||
<Input hidden />
|
<Input hidden />
|
||||||
<Input
|
<Input
|
||||||
id={`${this.props.name}Render`}
|
id={`${props.name}Render`}
|
||||||
onChange={this.onChange}
|
onChange={onChange}
|
||||||
addonAfter={!this.props.disabled && addon}
|
addonAfter={!props.disabled && addon}
|
||||||
style={{ fontFamily: "monospace" }}
|
className="input-code"
|
||||||
value={this.state.value}
|
value={value}
|
||||||
disabled={this.props.disabled}
|
disabled={props.disabled}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EuiInput;
|
export default EuiInput;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Link, withRouter, RouteComponentProps } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Button, Menu, Dropdown, Input, AutoComplete } from "antd";
|
import { Button, Menu, Dropdown, Input, AutoComplete } from "antd";
|
||||||
import { UserOutlined, DownOutlined, QuestionOutlined } from "@ant-design/icons";
|
import { UserOutlined, DownOutlined, QuestionOutlined } from "@ant-design/icons";
|
||||||
@ -14,15 +14,6 @@ import {
|
|||||||
import InternalStore from "../stores/InternalStore";
|
import InternalStore from "../stores/InternalStore";
|
||||||
import SessionStore from "../stores/SessionStore";
|
import SessionStore from "../stores/SessionStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
|
||||||
user: User;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IState {
|
|
||||||
searchResult?: GlobalSearchResponse;
|
|
||||||
settings?: SettingsResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderTitle = (title: string) => <span>{title}</span>;
|
const renderTitle = (title: string) => <span>{title}</span>;
|
||||||
|
|
||||||
const renderItem = (title: string, url: string) => ({
|
const renderItem = (title: string, url: string) => ({
|
||||||
@ -30,22 +21,19 @@ const renderItem = (title: string, url: string) => ({
|
|||||||
label: <Link to={url}>{title}</Link>,
|
label: <Link to={url}>{title}</Link>,
|
||||||
});
|
});
|
||||||
|
|
||||||
class Header extends Component<IProps, IState> {
|
function Header({ user }: { user: User }) {
|
||||||
constructor(props: IProps) {
|
const navigate = useNavigate();
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {};
|
const [settings, setSettings] = useState<SettingsResponse | undefined>(undefined);
|
||||||
}
|
const [searchResult, setSearchResult] = useState<GlobalSearchResponse | undefined>(undefined);
|
||||||
|
|
||||||
componentDidMount() {
|
useEffect(() => {
|
||||||
InternalStore.settings((resp: SettingsResponse) => {
|
InternalStore.settings((resp: SettingsResponse) => {
|
||||||
this.setState({
|
setSettings(resp);
|
||||||
settings: resp,
|
|
||||||
});
|
});
|
||||||
});
|
}, [user]);
|
||||||
}
|
|
||||||
|
|
||||||
onSearch = (search: string) => {
|
const onSearch = (search: string) => {
|
||||||
if (search.length < 3) {
|
if (search.length < 3) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -55,14 +43,11 @@ class Header extends Component<IProps, IState> {
|
|||||||
req.setSearch(search);
|
req.setSearch(search);
|
||||||
|
|
||||||
InternalStore.globalSearch(req, (resp: GlobalSearchResponse) => {
|
InternalStore.globalSearch(req, (resp: GlobalSearchResponse) => {
|
||||||
this.setState({
|
setSearchResult(resp);
|
||||||
searchResult: resp,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onLogout = () => {
|
const onLogout = () => {
|
||||||
let settings = this.state.settings;
|
|
||||||
if (settings === undefined) {
|
if (settings === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -71,30 +56,29 @@ class Header extends Component<IProps, IState> {
|
|||||||
|
|
||||||
if (!oidc.getEnabled() || oidc.getLogoutUrl() === "") {
|
if (!oidc.getEnabled() || oidc.getLogoutUrl() === "") {
|
||||||
SessionStore.logout(true, () => {
|
SessionStore.logout(true, () => {
|
||||||
this.props.history.push("/login");
|
navigate("/login");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
SessionStore.logout(false, () => {
|
SessionStore.logout(false, () => {
|
||||||
window.location.assign(oidc.getLogoutUrl());
|
navigate(oidc.getLogoutUrl());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (settings === undefined) {
|
||||||
if (this.state.settings === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let oidcEnabled = this.state.settings!.getOpenidConnect()!.getEnabled();
|
let oidcEnabled = settings!.getOpenidConnect()!.getEnabled();
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
{!oidcEnabled && (
|
{!oidcEnabled && (
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<Link to={`/users/${this.props.user.getId()}/password`}>Change password</Link>
|
<Link to={`/users/${user.getId()}/password`}>Change password</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
<Menu.Item onClick={this.onLogout}>Logout</Menu.Item>
|
<Menu.Item onClick={onLogout}>Logout</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -120,8 +104,8 @@ class Header extends Component<IProps, IState> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.state.searchResult !== undefined) {
|
if (searchResult !== undefined) {
|
||||||
for (const res of this.state.searchResult.getResultList()) {
|
for (const res of searchResult.getResultList()) {
|
||||||
if (res.getKind() === "tenant") {
|
if (res.getKind() === "tenant") {
|
||||||
options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`));
|
options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`));
|
||||||
}
|
}
|
||||||
@ -134,10 +118,7 @@ class Header extends Component<IProps, IState> {
|
|||||||
|
|
||||||
if (res.getKind() === "application") {
|
if (res.getKind() === "application") {
|
||||||
options[2].options.push(
|
options[2].options.push(
|
||||||
renderItem(
|
renderItem(res.getApplicationName(), `/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}`),
|
||||||
res.getApplicationName(),
|
|
||||||
`/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}`,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +142,7 @@ class Header extends Component<IProps, IState> {
|
|||||||
dropdownClassName="search-dropdown"
|
dropdownClassName="search-dropdown"
|
||||||
dropdownMatchSelectWidth={500}
|
dropdownMatchSelectWidth={500}
|
||||||
options={options}
|
options={options}
|
||||||
onSearch={this.onSearch}
|
onSearch={onSearch}
|
||||||
>
|
>
|
||||||
<Input.Search placeholder="Search..." style={{ width: 500, marginTop: -5 }} />
|
<Input.Search placeholder="Search..." style={{ width: 500, marginTop: -5 }} />
|
||||||
</AutoComplete>
|
</AutoComplete>
|
||||||
@ -174,7 +155,7 @@ class Header extends Component<IProps, IState> {
|
|||||||
<div className="user">
|
<div className="user">
|
||||||
<Dropdown overlay={menu} placement="bottomRight" trigger={["click"]}>
|
<Dropdown overlay={menu} placement="bottomRight" trigger={["click"]}>
|
||||||
<Button type="primary" icon={<UserOutlined />}>
|
<Button type="primary" icon={<UserOutlined />}>
|
||||||
{this.props.user.getEmail()} <DownOutlined />
|
{user.getEmail()} <DownOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
@ -182,6 +163,5 @@ class Header extends Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(Header);
|
export default Header;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import JSONTreeOriginal from "react-json-tree";
|
import JSONTreeOriginal from "react-json-tree";
|
||||||
import fileDownload from "js-file-download";
|
import fileDownload from "js-file-download";
|
||||||
@ -12,55 +13,38 @@ interface IProps {
|
|||||||
logs: LogItem[];
|
logs: LogItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function LogTable(props: IProps) {
|
||||||
drawerOpen: boolean;
|
const [drawerOpen, setDrawerOpen] = useState<boolean>(false);
|
||||||
body: any;
|
const [body, setBody] = useState<any>(null);
|
||||||
drawerTitle: any;
|
const [drawerTitle, setDrawerTitle] = useState<any>(null);
|
||||||
}
|
|
||||||
|
|
||||||
class LogTable extends Component<IProps, IState> {
|
const onDrawerClose = () => {
|
||||||
constructor(props: IProps) {
|
setDrawerOpen(false);
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
drawerOpen: false,
|
|
||||||
body: null,
|
|
||||||
drawerTitle: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onDrawerClose = () => {
|
|
||||||
this.setState({
|
|
||||||
drawerOpen: false,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onDrawerOpen = (time: any, body: any) => {
|
const onDrawerOpen = (time: any, body: any) => {
|
||||||
let ts = new Date(0);
|
let ts = new Date(0);
|
||||||
ts.setUTCSeconds(time.seconds);
|
ts.setUTCSeconds(time.seconds);
|
||||||
let drawerTitle = moment(ts).format("YYYY-MM-DD HH:mm:ss");
|
let drawerTitle = moment(ts).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
this.setState({
|
setBody(body);
|
||||||
body: body,
|
setDrawerTitle(drawerTitle);
|
||||||
drawerTitle: drawerTitle,
|
setDrawerOpen(true);
|
||||||
drawerOpen: true,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
downloadSingleFrame = () => {
|
const downloadSingleFrame = () => {
|
||||||
fileDownload(JSON.stringify(JSON.parse(this.state.body), null, 4), "single-log.json", "application/json");
|
fileDownload(JSON.stringify(JSON.parse(body), null, 4), "single-log.json", "application/json");
|
||||||
};
|
};
|
||||||
|
|
||||||
downloadFrames = () => {
|
const downloadFrames = () => {
|
||||||
let items = this.props.logs.map((l, i) => JSON.parse(l.getBody()));
|
let items = props.logs.map((l, i) => JSON.parse(l.getBody()));
|
||||||
fileDownload(JSON.stringify(items, null, 4), "log.json");
|
fileDownload(JSON.stringify(items, null, 4), "log.json");
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
let items = props.logs.map((l, i) => l.toObject());
|
||||||
let items = this.props.logs.map((l, i) => l.toObject());
|
let bodyJson = JSON.parse(body);
|
||||||
let body = JSON.parse(this.state.body);
|
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
scheme: "google",
|
scheme: "google",
|
||||||
@ -86,15 +70,15 @@ class LogTable extends Component<IProps, IState> {
|
|||||||
return (
|
return (
|
||||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||||
<Drawer
|
<Drawer
|
||||||
title={`Details: ${this.state.drawerTitle}`}
|
title={`Details: ${drawerTitle}`}
|
||||||
placement="right"
|
placement="right"
|
||||||
width={650}
|
width={650}
|
||||||
onClose={this.onDrawerClose}
|
onClose={onDrawerClose}
|
||||||
visible={this.state.drawerOpen}
|
visible={drawerOpen}
|
||||||
extra={<Button onClick={this.downloadSingleFrame}>Download</Button>}
|
extra={<Button onClick={downloadSingleFrame}>Download</Button>}
|
||||||
>
|
>
|
||||||
<JSONTreeOriginal
|
<JSONTreeOriginal
|
||||||
data={body}
|
data={bodyJson}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
hideRoot={true}
|
hideRoot={true}
|
||||||
shouldExpandNode={() => {
|
shouldExpandNode={() => {
|
||||||
@ -105,7 +89,7 @@ class LogTable extends Component<IProps, IState> {
|
|||||||
{items.length !== 0 && (
|
{items.length !== 0 && (
|
||||||
<Space direction="horizontal" style={{ float: "right" }} size="large">
|
<Space direction="horizontal" style={{ float: "right" }} size="large">
|
||||||
<Spin size="small" />
|
<Spin size="small" />
|
||||||
<Button onClick={this.downloadFrames}>Download</Button>
|
<Button onClick={downloadFrames}>Download</Button>
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
<Table
|
<Table
|
||||||
@ -136,7 +120,7 @@ class LogTable extends Component<IProps, IState> {
|
|||||||
type="primary"
|
type="primary"
|
||||||
shape="round"
|
shape="round"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={this.onDrawerOpen(obj.time, obj.body)}
|
onClick={onDrawerOpen(obj.time, obj.body)}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</Button>
|
</Button>
|
||||||
@ -166,6 +150,5 @@ class LogTable extends Component<IProps, IState> {
|
|||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default LogTable;
|
export default LogTable;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useEffect, PropsWithChildren } from "react";
|
||||||
|
|
||||||
import L, { LatLngTuple, FitBoundsOptions } from "leaflet";
|
import L, { LatLngTuple, FitBoundsOptions } from "leaflet";
|
||||||
import "leaflet.awesome-markers";
|
import "leaflet.awesome-markers";
|
||||||
import { MarkerProps as LMarkerProps } from "react-leaflet";
|
import { MarkerProps as LMarkerProps, useMap } from "react-leaflet";
|
||||||
import { MapContainer, Marker as LMarker, TileLayer } from "react-leaflet";
|
import { MapContainer, Marker as LMarker, TileLayer } from "react-leaflet";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -12,78 +12,49 @@ interface IProps {
|
|||||||
boundsOptions?: FitBoundsOptions;
|
boundsOptions?: FitBoundsOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function MapControl(props: { center?: [number, number]; bounds?: LatLngTuple[]; boundsOptions?: FitBoundsOptions }) {
|
||||||
map?: L.Map;
|
const map = useMap();
|
||||||
}
|
|
||||||
|
|
||||||
class Map extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
if (map === undefined) {
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
setMap = (map: L.Map) => {
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
map: map,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
// This is needed as setMap is called after the map has been created.
|
|
||||||
// There is a small amount of time where componentDidUpdate can't update
|
|
||||||
// the map with the new center because setMap hasn't been called yet.
|
|
||||||
// In such case, the map would never update to the new center.
|
|
||||||
if (this.props.center !== undefined) {
|
|
||||||
map.panTo(this.props.center);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.bounds !== undefined) {
|
|
||||||
map.fitBounds(this.props.bounds, this.props.boundsOptions);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidUpdate(oldProps: IProps) {
|
|
||||||
if (this.props === oldProps) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.map) {
|
if (props.center !== undefined) {
|
||||||
if (this.props.center !== undefined) {
|
map.flyTo(props.center);
|
||||||
this.state.map.flyTo(this.props.center);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.bounds !== undefined) {
|
if (props.bounds !== undefined) {
|
||||||
this.state.map.flyToBounds(this.props.bounds, this.props.boundsOptions);
|
map.flyToBounds(props.bounds, props.boundsOptions);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
function Map(props: PropsWithChildren<IProps>) {
|
||||||
const style = {
|
const style = {
|
||||||
height: this.props.height,
|
height: props.height,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MapContainer
|
<MapContainer
|
||||||
bounds={this.props.bounds}
|
bounds={props.bounds}
|
||||||
boundsOptions={this.props.boundsOptions}
|
boundsOptions={props.boundsOptions}
|
||||||
center={this.props.center}
|
center={props.center}
|
||||||
zoom={13}
|
zoom={13}
|
||||||
scrollWheelZoom={false}
|
scrollWheelZoom={false}
|
||||||
animate={true}
|
|
||||||
style={style}
|
style={style}
|
||||||
whenCreated={this.setMap}
|
|
||||||
>
|
>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
/>
|
/>
|
||||||
{this.props.children}
|
{props.children}
|
||||||
|
<MapControl bounds={props.bounds} boundsOptions={props.boundsOptions} center={props.center} />
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export type MarkerColor =
|
export type MarkerColor =
|
||||||
| "red"
|
| "red"
|
||||||
@ -103,9 +74,8 @@ interface MarkerProps extends LMarkerProps {
|
|||||||
color: MarkerColor;
|
color: MarkerColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Marker extends Component<MarkerProps> {
|
export function Marker(props: MarkerProps) {
|
||||||
render() {
|
const { faIcon, color, position, ...otherProps } = props;
|
||||||
const { faIcon, color, position, ...otherProps } = this.props;
|
|
||||||
|
|
||||||
const iconMarker = L.AwesomeMarkers.icon({
|
const iconMarker = L.AwesomeMarkers.icon({
|
||||||
icon: faIcon,
|
icon: faIcon,
|
||||||
@ -115,10 +85,9 @@ export class Marker extends Component<MarkerProps> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<LMarker icon={iconMarker} position={position} {...otherProps}>
|
<LMarker icon={iconMarker} position={position} {...otherProps}>
|
||||||
{this.props.children}
|
{props.children}
|
||||||
</LMarker>
|
</LMarker>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default Map;
|
export default Map;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { withRouter, RouteComponentProps, Link } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Menu, MenuProps } from "antd";
|
import { Menu, MenuProps } from "antd";
|
||||||
import {
|
import {
|
||||||
@ -24,46 +24,18 @@ import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "../compon
|
|||||||
import TenantStore from "../stores/TenantStore";
|
import TenantStore from "../stores/TenantStore";
|
||||||
import SessionStore from "../stores/SessionStore";
|
import SessionStore from "../stores/SessionStore";
|
||||||
|
|
||||||
interface IState {
|
function SideMenu() {
|
||||||
tenantId: string;
|
const [tenantId, setTenantId] = useState<string>("");
|
||||||
selectedKey: string;
|
const [selectedKey, setSelectedKey] = useState<string>("");
|
||||||
}
|
|
||||||
|
|
||||||
class SideMenu extends Component<RouteComponentProps, IState> {
|
const location = useLocation();
|
||||||
constructor(props: RouteComponentProps) {
|
const navigate = useNavigate();
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
const setTenant = () => {
|
||||||
tenantId: "",
|
setTenantId(SessionStore.getTenantId());
|
||||||
selectedKey: "ns-dashboard",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
SessionStore.on("tenant.change", this.setTenant);
|
|
||||||
this.setTenant();
|
|
||||||
this.parseLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
SessionStore.removeListener("tenant.change", this.setTenant);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: RouteComponentProps) {
|
|
||||||
if (this.props === prevProps) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.parseLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
setTenant = () => {
|
|
||||||
this.setState({
|
|
||||||
tenantId: SessionStore.getTenantId(),
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
getTenantOptions = (search: string, fn: OptionsCallbackFunc) => {
|
const getTenantOptions = (search: string, fn: OptionsCallbackFunc) => {
|
||||||
let req = new ListTenantsRequest();
|
let req = new ListTenantsRequest();
|
||||||
req.setSearch(search);
|
req.setSearch(search);
|
||||||
req.setLimit(10);
|
req.setLimit(10);
|
||||||
@ -76,7 +48,7 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getTenantOption = (id: string, fn: OptionCallbackFunc) => {
|
const getTenantOption = (id: string, fn: OptionCallbackFunc) => {
|
||||||
TenantStore.get(id, (resp: GetTenantResponse) => {
|
TenantStore.get(id, (resp: GetTenantResponse) => {
|
||||||
const tenant = resp.getTenant();
|
const tenant = resp.getTenant();
|
||||||
if (tenant) {
|
if (tenant) {
|
||||||
@ -85,82 +57,94 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onTenantSelect = (value: string) => {
|
const onTenantSelect = (value: string) => {
|
||||||
SessionStore.setTenantId(value);
|
SessionStore.setTenantId(value);
|
||||||
this.props.history.push(`/tenants/${value}`);
|
navigate(`/tenants/${value}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
parseLocation = () => {
|
const parseLocation = () => {
|
||||||
const path = this.props.history.location.pathname;
|
const path = location.pathname;
|
||||||
const tenantRe = /\/tenants\/([\w-]{36})/g;
|
const tenantRe = /\/tenants\/([\w-]{36})/g;
|
||||||
const match = tenantRe.exec(path);
|
const match = tenantRe.exec(path);
|
||||||
|
|
||||||
if (match !== null && this.state.tenantId !== match[1]) {
|
if (match !== null && tenantId !== match[1]) {
|
||||||
SessionStore.setTenantId(match[1]);
|
SessionStore.setTenantId(match[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ns dashboard
|
// ns dashboard
|
||||||
if (path === "/dashboard") {
|
if (path === "/dashboard") {
|
||||||
this.setState({ selectedKey: "ns-dashboard" });
|
setSelectedKey("ns-dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ns tenants
|
// ns tenants
|
||||||
if (/\/tenants(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
if (/\/tenants(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "ns-tenants" });
|
setSelectedKey("ns-tenants");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ns tenants
|
// ns tenants
|
||||||
if (/\/users(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
if (/\/users(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "ns-users" });
|
setSelectedKey("ns-users");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ns api keys
|
// ns api keys
|
||||||
if (/\/api-keys(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
if (/\/api-keys(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "ns-api-keys" });
|
setSelectedKey("ns-api-keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ns device-profile templates
|
// ns device-profile templates
|
||||||
if (/\/device-profile-templates(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
if (/\/device-profile-templates(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "ns-device-profile-templates" });
|
setSelectedKey("ns-device-profile-templates");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/\/regions.*/g.exec(path)) {
|
if (/\/regions.*/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "ns-regions" });
|
setSelectedKey("ns-regions");
|
||||||
}
|
}
|
||||||
|
|
||||||
// tenant dashboard
|
// tenant dashboard
|
||||||
if (/\/tenants\/[\w-]{36}/g.exec(path)) {
|
if (/\/tenants\/[\w-]{36}/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "tenant-dashboard" });
|
setSelectedKey("tenant-dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
// tenant users
|
// tenant users
|
||||||
if (/\/tenants\/[\w-]{36}\/users.*/g.exec(path)) {
|
if (/\/tenants\/[\w-]{36}\/users.*/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "tenant-users" });
|
setSelectedKey("tenant-users");
|
||||||
}
|
}
|
||||||
|
|
||||||
// tenant api-keys
|
// tenant api-keys
|
||||||
if (/\/tenants\/[\w-]{36}\/api-keys.*/g.exec(path)) {
|
if (/\/tenants\/[\w-]{36}\/api-keys.*/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "tenant-api-keys" });
|
setSelectedKey("tenant-api-keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
// tenant device-profiles
|
// tenant device-profiles
|
||||||
if (/\/tenants\/[\w-]{36}\/device-profiles.*/g.exec(path)) {
|
if (/\/tenants\/[\w-]{36}\/device-profiles.*/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "tenant-device-profiles" });
|
setSelectedKey("tenant-device-profiles");
|
||||||
}
|
}
|
||||||
|
|
||||||
// tenant gateways
|
// tenant gateways
|
||||||
if (/\/tenants\/[\w-]{36}\/gateways.*/g.exec(path)) {
|
if (/\/tenants\/[\w-]{36}\/gateways.*/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "tenant-gateways" });
|
setSelectedKey("tenant-gateways");
|
||||||
}
|
}
|
||||||
|
|
||||||
// tenant applications
|
// tenant applications
|
||||||
if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) {
|
if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) {
|
||||||
this.setState({ selectedKey: "tenant-applications" });
|
setSelectedKey("tenant-applications");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
useEffect(() => {
|
||||||
const tenantId = this.state.tenantId;
|
SessionStore.on("tenant.change", setTenant);
|
||||||
|
setTenant();
|
||||||
|
parseLocation();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
SessionStore.removeListener("tenant.change", setTenant);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
parseLocation();
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
let items: MenuProps["items"] = [];
|
let items: MenuProps["items"] = [];
|
||||||
|
|
||||||
if (SessionStore.isAdmin()) {
|
if (SessionStore.isAdmin()) {
|
||||||
@ -169,16 +153,36 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
|||||||
label: "Network Server",
|
label: "Network Server",
|
||||||
icon: <CloudOutlined />,
|
icon: <CloudOutlined />,
|
||||||
children: [
|
children: [
|
||||||
{ key: "ns-dashboard", icon: <DashboardOutlined />, label: <Link to="/dashboard">Dashboard</Link> },
|
{
|
||||||
{ key: "ns-tenants", icon: <HomeOutlined />, label: <Link to="/tenants">Tenants</Link> },
|
key: "ns-dashboard",
|
||||||
{ key: "ns-users", icon: <UserOutlined />, label: <Link to="/users">Users</Link> },
|
icon: <DashboardOutlined />,
|
||||||
{ key: "ns-api-keys", icon: <KeyOutlined />, label: <Link to="/api-keys">API Keys</Link> },
|
label: <Link to="/dashboard">Dashboard</Link>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "ns-tenants",
|
||||||
|
icon: <HomeOutlined />,
|
||||||
|
label: <Link to="/tenants">Tenants</Link>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "ns-users",
|
||||||
|
icon: <UserOutlined />,
|
||||||
|
label: <Link to="/users">Users</Link>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "ns-api-keys",
|
||||||
|
icon: <KeyOutlined />,
|
||||||
|
label: <Link to="/api-keys">API Keys</Link>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "ns-device-profile-templates",
|
key: "ns-device-profile-templates",
|
||||||
icon: <ControlOutlined />,
|
icon: <ControlOutlined />,
|
||||||
label: <Link to="/device-profile-templates">Device Profile Templates</Link>,
|
label: <Link to="/device-profile-templates">Device Profile Templates</Link>,
|
||||||
},
|
},
|
||||||
{ key: "ns-regions", icon: <CompassOutlined />, label: <Link to="/regions">Regions</Link> },
|
{
|
||||||
|
key: "ns-regions",
|
||||||
|
icon: <CompassOutlined />,
|
||||||
|
label: <Link to="/regions">Regions</Link>,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -186,7 +190,13 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
|||||||
key: "ns",
|
key: "ns",
|
||||||
label: "Network Server",
|
label: "Network Server",
|
||||||
icon: <CloudOutlined />,
|
icon: <CloudOutlined />,
|
||||||
children: [{ key: "ns-regions", icon: <CompassOutlined />, label: <Link to="/regions">Regions</Link> }],
|
children: [
|
||||||
|
{
|
||||||
|
key: "ns-regions",
|
||||||
|
icon: <CompassOutlined />,
|
||||||
|
label: <Link to="/regions">Regions</Link>,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +211,11 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
|||||||
icon: <DashboardOutlined />,
|
icon: <DashboardOutlined />,
|
||||||
label: <Link to={`/tenants/${tenantId}`}>Dashboard</Link>,
|
label: <Link to={`/tenants/${tenantId}`}>Dashboard</Link>,
|
||||||
},
|
},
|
||||||
{ key: "tenant-users", icon: <UserOutlined />, label: <Link to={`/tenants/${tenantId}/users`}>Users</Link> },
|
{
|
||||||
|
key: "tenant-users",
|
||||||
|
icon: <UserOutlined />,
|
||||||
|
label: <Link to={`/tenants/${tenantId}/users`}>Users</Link>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "tenant-api-keys",
|
key: "tenant-api-keys",
|
||||||
icon: <KeyOutlined />,
|
icon: <KeyOutlined />,
|
||||||
@ -231,21 +245,20 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
|||||||
<Autocomplete
|
<Autocomplete
|
||||||
placeholder="Select tenant"
|
placeholder="Select tenant"
|
||||||
className="organiation-select"
|
className="organiation-select"
|
||||||
getOption={this.getTenantOption}
|
getOption={getTenantOption}
|
||||||
getOptions={this.getTenantOptions}
|
getOptions={getTenantOptions}
|
||||||
onSelect={this.onTenantSelect}
|
onSelect={onTenantSelect}
|
||||||
value={this.state.tenantId}
|
value={tenantId}
|
||||||
/>
|
/>
|
||||||
<Menu
|
<Menu
|
||||||
mode="inline"
|
mode="inline"
|
||||||
openKeys={["ns", "tenant"]}
|
openKeys={["ns", "tenant"]}
|
||||||
selectedKeys={[this.state.selectedKey]}
|
selectedKeys={[selectedKey]}
|
||||||
expandIcon={<div></div>}
|
expandIcon={<div></div>}
|
||||||
items={items}
|
items={items}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(SideMenu);
|
export default SideMenu;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
import { TimeUnit } from "chart.js";
|
import { TimeUnit } from "chart.js";
|
||||||
@ -13,12 +11,11 @@ interface IProps {
|
|||||||
aggregation: Aggregation;
|
aggregation: Aggregation;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MetricBar extends Component<IProps> {
|
function MetricBar(props: IProps) {
|
||||||
render() {
|
|
||||||
let unit: TimeUnit = "hour";
|
let unit: TimeUnit = "hour";
|
||||||
if (this.props.aggregation === Aggregation.DAY) {
|
if (props.aggregation === Aggregation.DAY) {
|
||||||
unit = "day";
|
unit = "day";
|
||||||
} else if (this.props.aggregation === Aggregation.MONTH) {
|
} else if (props.aggregation === Aggregation.MONTH) {
|
||||||
unit = "month";
|
unit = "month";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,11 +68,11 @@ class MetricBar extends Component<IProps> {
|
|||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
}[];
|
}[];
|
||||||
} = {
|
} = {
|
||||||
labels: this.props.metric.getTimestampsList().map(v => moment(v.toDate()).valueOf()),
|
labels: props.metric.getTimestampsList().map(v => moment(v.toDate()).valueOf()),
|
||||||
datasets: [],
|
datasets: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let ds of this.props.metric.getDatasetsList()) {
|
for (let ds of props.metric.getDatasetsList()) {
|
||||||
data.datasets.push({
|
data.datasets.push({
|
||||||
label: ds.getLabel(),
|
label: ds.getLabel(),
|
||||||
data: ds.getDataList(),
|
data: ds.getDataList(),
|
||||||
@ -84,11 +81,10 @@ class MetricBar extends Component<IProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={this.props.metric.getName()} className="dashboard-chart">
|
<Card title={props.metric.getName()} className="dashboard-chart">
|
||||||
<Bar data={data} options={options} />
|
<Bar data={data} options={options} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default MetricBar;
|
export default MetricBar;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
import { TimeUnit } from "chart.js";
|
import { TimeUnit } from "chart.js";
|
||||||
@ -14,14 +12,13 @@ interface IProps {
|
|||||||
zeroToNull?: boolean;
|
zeroToNull?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MetricChart extends Component<IProps> {
|
function MetricChart(props: IProps) {
|
||||||
render() {
|
|
||||||
let unit: TimeUnit = "hour";
|
let unit: TimeUnit = "hour";
|
||||||
let tooltipFormat = "LT";
|
let tooltipFormat = "LT";
|
||||||
if (this.props.aggregation === Aggregation.DAY) {
|
if (props.aggregation === Aggregation.DAY) {
|
||||||
unit = "day";
|
unit = "day";
|
||||||
tooltipFormat = "MMM Do";
|
tooltipFormat = "MMM Do";
|
||||||
} else if (this.props.aggregation === Aggregation.MONTH) {
|
} else if (props.aggregation === Aggregation.MONTH) {
|
||||||
unit = "month";
|
unit = "month";
|
||||||
tooltipFormat = "MMM YYYY";
|
tooltipFormat = "MMM YYYY";
|
||||||
}
|
}
|
||||||
@ -52,8 +49,8 @@ class MetricChart extends Component<IProps> {
|
|||||||
|
|
||||||
let prevValue = 0;
|
let prevValue = 0;
|
||||||
let data = {
|
let data = {
|
||||||
labels: this.props.metric.getTimestampsList().map(v => moment(v.toDate()).valueOf()),
|
labels: props.metric.getTimestampsList().map(v => moment(v.toDate()).valueOf()),
|
||||||
datasets: this.props.metric.getDatasetsList().map(v => {
|
datasets: props.metric.getDatasetsList().map(v => {
|
||||||
return {
|
return {
|
||||||
label: v.getLabel(),
|
label: v.getLabel(),
|
||||||
borderColor: "rgba(33, 150, 243, 1)",
|
borderColor: "rgba(33, 150, 243, 1)",
|
||||||
@ -61,10 +58,10 @@ class MetricChart extends Component<IProps> {
|
|||||||
lineTension: 0,
|
lineTension: 0,
|
||||||
pointBackgroundColor: "rgba(33, 150, 243, 1)",
|
pointBackgroundColor: "rgba(33, 150, 243, 1)",
|
||||||
data: v.getDataList().map(v => {
|
data: v.getDataList().map(v => {
|
||||||
if (v === 0 && this.props.zeroToNull) {
|
if (v === 0 && props.zeroToNull) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
if (this.props.metric.getKind() === MetricKind.COUNTER) {
|
if (props.metric.getKind() === MetricKind.COUNTER) {
|
||||||
let val = v - prevValue;
|
let val = v - prevValue;
|
||||||
prevValue = v;
|
prevValue = v;
|
||||||
if (val < 0) {
|
if (val < 0) {
|
||||||
@ -80,8 +77,8 @@ class MetricChart extends Component<IProps> {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = this.props.metric.getName();
|
let name = props.metric.getName();
|
||||||
if (this.props.metric.getKind() === MetricKind.COUNTER) {
|
if (props.metric.getKind() === MetricKind.COUNTER) {
|
||||||
name = `${name} (per ${unit})`;
|
name = `${name} (per ${unit})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +88,5 @@ class MetricChart extends Component<IProps> {
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default MetricChart;
|
export default MetricChart;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
import { color } from "chart.js/helpers";
|
import { color } from "chart.js/helpers";
|
||||||
@ -16,12 +14,11 @@ interface IProps {
|
|||||||
aggregation: Aggregation;
|
aggregation: Aggregation;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MetricHeatmap extends Component<IProps> {
|
function MetricHeatmap(props: IProps) {
|
||||||
render() {
|
|
||||||
let unit: TimeUnit = "hour";
|
let unit: TimeUnit = "hour";
|
||||||
if (this.props.aggregation === Aggregation.DAY) {
|
if (props.aggregation === Aggregation.DAY) {
|
||||||
unit = "day";
|
unit = "day";
|
||||||
} else if (this.props.aggregation === Aggregation.MONTH) {
|
} else if (props.aggregation === Aggregation.MONTH) {
|
||||||
unit = "month";
|
unit = "month";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +41,7 @@ class MetricHeatmap extends Component<IProps> {
|
|||||||
unit: unit,
|
unit: unit,
|
||||||
},
|
},
|
||||||
offset: true,
|
offset: true,
|
||||||
labels: this.props.metric.getTimestampsList().map(v => moment(v.toDate().valueOf())),
|
labels: props.metric.getTimestampsList().map(v => moment(v.toDate().valueOf())),
|
||||||
grid: {
|
grid: {
|
||||||
display: false,
|
display: false,
|
||||||
},
|
},
|
||||||
@ -73,15 +70,15 @@ class MetricHeatmap extends Component<IProps> {
|
|||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
labels: this.props.metric.getDatasetsList().map(v => v.getLabel()),
|
labels: props.metric.getDatasetsList().map(v => v.getLabel()),
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "Heatmap",
|
label: "Heatmap",
|
||||||
data: dataData,
|
data: dataData,
|
||||||
minValue: -1,
|
minValue: -1,
|
||||||
maxValue: -1,
|
maxValue: -1,
|
||||||
fromColor: this.props.fromColor.match(/\d+/g)!.map(Number),
|
fromColor: props.fromColor.match(/\d+/g)!.map(Number),
|
||||||
toColor: this.props.toColor.match(/\d+/g)!.map(Number),
|
toColor: props.toColor.match(/\d+/g)!.map(Number),
|
||||||
backgroundColor: (ctx: any): string => {
|
backgroundColor: (ctx: any): string => {
|
||||||
if (
|
if (
|
||||||
ctx.dataset === undefined ||
|
ctx.dataset === undefined ||
|
||||||
@ -105,10 +102,10 @@ class MetricHeatmap extends Component<IProps> {
|
|||||||
},
|
},
|
||||||
borderWidth: 0,
|
borderWidth: 0,
|
||||||
width: (ctx: any) => {
|
width: (ctx: any) => {
|
||||||
return (ctx.chart.chartArea || {}).width / this.props.metric.getTimestampsList().length - 1;
|
return (ctx.chart.chartArea || {}).width / props.metric.getTimestampsList().length - 1;
|
||||||
},
|
},
|
||||||
height: (ctx: any) => {
|
height: (ctx: any) => {
|
||||||
return (ctx.chart.chartArea || {}).height / this.props.metric.getDatasetsList().length - 1;
|
return (ctx.chart.chartArea || {}).height / props.metric.getDatasetsList().length - 1;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -116,8 +113,8 @@ class MetricHeatmap extends Component<IProps> {
|
|||||||
|
|
||||||
data.labels.sort();
|
data.labels.sort();
|
||||||
|
|
||||||
const tsList = this.props.metric.getTimestampsList();
|
const tsList = props.metric.getTimestampsList();
|
||||||
const dsList = this.props.metric.getDatasetsList();
|
const dsList = props.metric.getDatasetsList();
|
||||||
|
|
||||||
for (let i = 0; i < tsList.length; i++) {
|
for (let i = 0; i < tsList.length; i++) {
|
||||||
for (let ds of dsList) {
|
for (let ds of dsList) {
|
||||||
@ -143,11 +140,10 @@ class MetricHeatmap extends Component<IProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={this.props.metric.getName()} className="dashboard-chart">
|
<Card title={props.metric.getName()} className="dashboard-chart">
|
||||||
<Chart type="matrix" data={data} options={options} />
|
<Chart type="matrix" data={data} options={options} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default MetricHeatmap;
|
export default MetricHeatmap;
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
padding-top: 85px;
|
padding-top: 85px;
|
||||||
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: fixed;
|
position: fixed !important;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
@ -117,5 +117,10 @@ pre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-drawer-body {
|
.ant-drawer-body {
|
||||||
padding-bottom: 88px; /* 64 + 24 */
|
padding-bottom: 88px;
|
||||||
|
/* 64 + 24 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-code input {
|
||||||
|
font-family: monospace !important;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom/client";
|
||||||
|
|
||||||
// import { Chart, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, Title } from "chart.js";
|
|
||||||
import { Chart, registerables } from "chart.js";
|
import { Chart, registerables } from "chart.js";
|
||||||
import { MatrixElement, MatrixController } from "chartjs-chart-matrix";
|
import { MatrixElement, MatrixController } from "chartjs-chart-matrix";
|
||||||
import "chartjs-adapter-moment";
|
import "chartjs-adapter-moment";
|
||||||
@ -9,7 +8,7 @@ import "chartjs-adapter-moment";
|
|||||||
import App from "./App";
|
import App from "./App";
|
||||||
import reportWebVitals from "./reportWebVitals";
|
import reportWebVitals from "./reportWebVitals";
|
||||||
|
|
||||||
import "antd/dist/antd.min.css";
|
import "antd/dist/reset.css";
|
||||||
import "leaflet/dist/leaflet.css";
|
import "leaflet/dist/leaflet.css";
|
||||||
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
|
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
|
||||||
import "@fortawesome/fontawesome-free/css/all.css";
|
import "@fortawesome/fontawesome-free/css/all.css";
|
||||||
@ -19,12 +18,8 @@ import "./index.css";
|
|||||||
|
|
||||||
Chart.register(MatrixController, MatrixElement, ...registerables);
|
Chart.register(MatrixController, MatrixElement, ...registerables);
|
||||||
|
|
||||||
ReactDOM.render(
|
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||||
<React.StrictMode>
|
root.render(<App />);
|
||||||
<App />
|
|
||||||
</React.StrictMode>,
|
|
||||||
document.getElementById("root"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
|
@ -111,6 +111,8 @@ class ApplicationStore extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.emit("change");
|
||||||
|
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Application updated",
|
message: "Application updated",
|
||||||
duration: 3,
|
duration: 3,
|
||||||
|
@ -31,7 +31,7 @@ class RelayStore extends EventEmitter {
|
|||||||
|
|
||||||
callbackFunc(resp);
|
callbackFunc(resp);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
addDevice = (req: AddRelayDeviceRequest, callbackFunc: () => void) => {
|
addDevice = (req: AddRelayDeviceRequest, callbackFunc: () => void) => {
|
||||||
this.client.addDevice(req, SessionStore.getMetadata(), err => {
|
this.client.addDevice(req, SessionStore.getMetadata(), err => {
|
||||||
@ -47,7 +47,7 @@ class RelayStore extends EventEmitter {
|
|||||||
|
|
||||||
callbackFunc();
|
callbackFunc();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
removeDevice = (req: RemoveRelayDeviceRequest, callbackFunc: () => void) => {
|
removeDevice = (req: RemoveRelayDeviceRequest, callbackFunc: () => void) => {
|
||||||
this.client.removeDevice(req, SessionStore.getMetadata(), err => {
|
this.client.removeDevice(req, SessionStore.getMetadata(), err => {
|
||||||
@ -63,7 +63,7 @@ class RelayStore extends EventEmitter {
|
|||||||
|
|
||||||
callbackFunc();
|
callbackFunc();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
listDevices = (req: ListRelayDevicesRequest, callbackFunc: (resp: ListRelayDevicesResponse) => void) => {
|
listDevices = (req: ListRelayDevicesRequest, callbackFunc: (resp: ListRelayDevicesResponse) => void) => {
|
||||||
this.client.listDevices(req, SessionStore.getMetadata(), (err, resp) => {
|
this.client.listDevices(req, SessionStore.getMetadata(), (err, resp) => {
|
||||||
@ -74,7 +74,7 @@ class RelayStore extends EventEmitter {
|
|||||||
|
|
||||||
callbackFunc(resp);
|
callbackFunc(resp);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const relayStore = new RelayStore();
|
const relayStore = new RelayStore();
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Button } from "antd";
|
import { Form, Input, Button } from "antd";
|
||||||
|
|
||||||
import { ApiKey } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
import { ApiKey } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||||
@ -9,18 +7,15 @@ interface IProps {
|
|||||||
onFinish: (obj: ApiKey) => void;
|
onFinish: (obj: ApiKey) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {}
|
function ApiKeyForm(props: IProps) {
|
||||||
|
const onFinish = (values: ApiKey.AsObject) => {
|
||||||
class ApiKeyForm extends Component<IProps, IState> {
|
|
||||||
onFinish = (values: ApiKey.AsObject) => {
|
|
||||||
let apiKey = new ApiKey();
|
let apiKey = new ApiKey();
|
||||||
apiKey.setName(values.name);
|
apiKey.setName(values.name);
|
||||||
this.props.onFinish(apiKey);
|
props.onFinish(apiKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -32,6 +27,5 @@ class ApiKeyForm extends Component<IProps, IState> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ApiKeyForm;
|
export default ApiKeyForm;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Input, Typography, Button, Space } from "antd";
|
import { Input, Typography, Button, Space } from "antd";
|
||||||
@ -9,8 +8,7 @@ interface IProps {
|
|||||||
createApiKeyResponse: CreateApiKeyResponse;
|
createApiKeyResponse: CreateApiKeyResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiKeyToken extends Component<IProps> {
|
function ApiKeyToken(props: IProps) {
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }}>
|
<Space direction="vertical" style={{ width: "100%" }}>
|
||||||
<Typography>
|
<Typography>
|
||||||
@ -19,13 +17,12 @@ class ApiKeyToken extends Component<IProps> {
|
|||||||
Please note that this token can only be retrieved once:
|
Please note that this token can only be retrieved once:
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input.TextArea rows={4} value={this.props.createApiKeyResponse.getToken()} />
|
<Input.TextArea rows={4} value={props.createApiKeyResponse.getToken()} />
|
||||||
<Button type="primary">
|
<Button type="primary">
|
||||||
<Link to="../api-keys">Back</Link>
|
<Link to="../api-keys">Back</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ApiKeyToken;
|
export default ApiKeyToken;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
import { Space, Breadcrumb, Card } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { ApiKey, CreateApiKeyRequest, CreateApiKeyResponse } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
import { ApiKey, CreateApiKeyRequest, CreateApiKeyResponse } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||||
|
|
||||||
@ -9,32 +10,20 @@ import ApiKeyForm from "./ApiKeyForm";
|
|||||||
import ApiKeyToken from "./ApiKeyToken";
|
import ApiKeyToken from "./ApiKeyToken";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
|
|
||||||
interface IProps {}
|
function CreateAdminApiKey() {
|
||||||
|
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
|
||||||
|
|
||||||
interface IState {
|
const onFinish = (obj: ApiKey) => {
|
||||||
createApiKeyResponse?: CreateApiKeyResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateAdminApiKey extends Component<IProps, IState> {
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: ApiKey) => {
|
|
||||||
obj.setIsAdmin(true);
|
obj.setIsAdmin(true);
|
||||||
|
|
||||||
let req = new CreateApiKeyRequest();
|
let req = new CreateApiKeyRequest();
|
||||||
req.setApiKey(obj);
|
req.setApiKey(obj);
|
||||||
|
|
||||||
InternalStore.createApiKey(req, (resp: CreateApiKeyResponse) => {
|
InternalStore.createApiKey(req, (resp: CreateApiKeyResponse) => {
|
||||||
this.setState({
|
setCreateApiKeyResponse(resp);
|
||||||
createApiKeyResponse: resp,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const apiKey = new ApiKey();
|
const apiKey = new ApiKey();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -58,12 +47,11 @@ class CreateAdminApiKey extends Component<IProps, IState> {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
{!this.state.createApiKeyResponse && <ApiKeyForm initialValues={apiKey} onFinish={this.onFinish} />}
|
{!createApiKeyResponse && <ApiKeyForm initialValues={apiKey} onFinish={onFinish} />}
|
||||||
{this.state.createApiKeyResponse && <ApiKeyToken createApiKeyResponse={this.state.createApiKeyResponse} />}
|
{createApiKeyResponse && <ApiKeyToken createApiKeyResponse={createApiKeyResponse} />}
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateAdminApiKey;
|
export default CreateAdminApiKey;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
import { Space, Breadcrumb, Card } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { ApiKey, CreateApiKeyRequest, CreateApiKeyResponse } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
import { ApiKey, CreateApiKeyRequest, CreateApiKeyResponse } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
@ -10,34 +11,24 @@ import ApiKeyForm from "./ApiKeyForm";
|
|||||||
import ApiKeyToken from "./ApiKeyToken";
|
import ApiKeyToken from "./ApiKeyToken";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
|
|
||||||
interface IState {
|
|
||||||
createApiKeyResponse?: CreateApiKeyResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateTenantApiKey extends Component<IProps, IState> {
|
function CreateTenantApiKey(props: IProps) {
|
||||||
constructor(props: IProps) {
|
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: ApiKey) => {
|
const onFinish = (obj: ApiKey) => {
|
||||||
obj.setTenantId(this.props.tenant.getId());
|
obj.setTenantId(props.tenant.getId());
|
||||||
|
|
||||||
let req = new CreateApiKeyRequest();
|
let req = new CreateApiKeyRequest();
|
||||||
req.setApiKey(obj);
|
req.setApiKey(obj);
|
||||||
|
|
||||||
InternalStore.createApiKey(req, (resp: CreateApiKeyResponse) => {
|
InternalStore.createApiKey(req, (resp: CreateApiKeyResponse) => {
|
||||||
this.setState({
|
setCreateApiKeyResponse(resp);
|
||||||
createApiKeyResponse: resp,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const apiKey = new ApiKey();
|
const apiKey = new ApiKey();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -50,12 +41,12 @@ class CreateTenantApiKey extends Component<IProps, IState> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/api-keys`}>API Keys</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/api-keys`}>API Keys</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -66,12 +57,11 @@ class CreateTenantApiKey extends Component<IProps, IState> {
|
|||||||
title="Add API key"
|
title="Add API key"
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
{!this.state.createApiKeyResponse && <ApiKeyForm initialValues={apiKey} onFinish={this.onFinish} />}
|
{!createApiKeyResponse && <ApiKeyForm initialValues={apiKey} onFinish={onFinish} />}
|
||||||
{this.state.createApiKeyResponse && <ApiKeyToken createApiKeyResponse={this.state.createApiKeyResponse} />}
|
{createApiKeyResponse && <ApiKeyToken createApiKeyResponse={createApiKeyResponse} />}
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateTenantApiKey;
|
export default CreateTenantApiKey;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { DeleteOutlined } from "@ant-design/icons";
|
import { DeleteOutlined } from "@ant-design/icons";
|
||||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
import { Space, Breadcrumb, Button } from "antd";
|
||||||
import { ColumnsType } from "antd/es/table";
|
import { ColumnsType } from "antd/es/table";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ListApiKeysRequest,
|
ListApiKeysRequest,
|
||||||
@ -17,22 +17,10 @@ import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
|||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
|
|
||||||
interface IProps {}
|
function ListAdminApiKeys() {
|
||||||
|
const [refreshKey, setRefreshKey] = useState<number>(1);
|
||||||
|
|
||||||
interface IState {
|
const columns: ColumnsType<ApiKey.AsObject> = [
|
||||||
refreshKey: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ListAdminApiKeys extends Component<IProps, IState> {
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
refreshKey: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
columns = (): ColumnsType<ApiKey.AsObject> => {
|
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
title: "ID",
|
title: "ID",
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
@ -50,29 +38,26 @@ class ListAdminApiKeys extends Component<IProps, IState> {
|
|||||||
key: "action",
|
key: "action",
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<DeleteConfirm typ="API key" confirm={record.name} onConfirm={this.deleteApiKey(record.id)}>
|
<DeleteConfirm typ="API key" confirm={record.name} onConfirm={deleteApiKey(record.id)}>
|
||||||
<Button shape="circle" icon={<DeleteOutlined />} />
|
<Button shape="circle" icon={<DeleteOutlined />} />
|
||||||
</DeleteConfirm>
|
</DeleteConfirm>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
deleteApiKey = (id: string): (() => void) => {
|
const deleteApiKey = (id: string): (() => void) => {
|
||||||
return () => {
|
return () => {
|
||||||
let req = new DeleteApiKeyRequest();
|
let req = new DeleteApiKeyRequest();
|
||||||
req.setId(id);
|
req.setId(id);
|
||||||
|
|
||||||
InternalStore.deleteApiKey(req, () => {
|
InternalStore.deleteApiKey(req, () => {
|
||||||
// trigger a data-table reload
|
// trigger a data-table reload
|
||||||
this.setState({
|
setRefreshKey(refreshKey + 1);
|
||||||
refreshKey: this.state.refreshKey + 1,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
const getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||||
let req = new ListApiKeysRequest();
|
let req = new ListApiKeysRequest();
|
||||||
req.setLimit(limit);
|
req.setLimit(limit);
|
||||||
req.setOffset(offset);
|
req.setOffset(offset);
|
||||||
@ -84,7 +69,6 @@ class ListAdminApiKeys extends Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
@ -105,10 +89,9 @@ class ListAdminApiKeys extends Component<IProps, IState> {
|
|||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" refreshKey={this.state.refreshKey} />
|
<DataTable columns={columns} getPage={getPage} rowKey="id" refreshKey={refreshKey} />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListAdminApiKeys;
|
export default ListAdminApiKeys;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { DeleteOutlined } from "@ant-design/icons";
|
import { DeleteOutlined } from "@ant-design/icons";
|
||||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
import { Space, Breadcrumb, Button } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
import { ColumnsType } from "antd/es/table";
|
import { ColumnsType } from "antd/es/table";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -20,23 +21,12 @@ import Admin from "../../components/Admin";
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
isAdmin: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function ListTenantApiKeys(props: IProps) {
|
||||||
refreshKey: number;
|
const [refreshKey, setRefreshKey] = useState<number>(1);
|
||||||
}
|
|
||||||
|
|
||||||
class ListTenantApiKeys extends Component<IProps, IState> {
|
const columns: ColumnsType<ApiKey.AsObject> = [
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
refreshKey: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
columns = (): ColumnsType<ApiKey.AsObject> => {
|
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
title: "ID",
|
title: "ID",
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
@ -54,35 +44,32 @@ class ListTenantApiKeys extends Component<IProps, IState> {
|
|||||||
key: "action",
|
key: "action",
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Admin tenantId={this.props.tenant.getId()} isTenantAdmin>
|
<Admin tenantId={props.tenant.getId()} isTenantAdmin>
|
||||||
<DeleteConfirm typ="API key" confirm={record.name} onConfirm={this.deleteApiKey(record.id)}>
|
<DeleteConfirm typ="API key" confirm={record.name} onConfirm={deleteApiKey(record.id)}>
|
||||||
<Button shape="circle" icon={<DeleteOutlined />} />
|
<Button shape="circle" icon={<DeleteOutlined />} />
|
||||||
</DeleteConfirm>
|
</DeleteConfirm>
|
||||||
</Admin>
|
</Admin>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
deleteApiKey = (id: string): (() => void) => {
|
const deleteApiKey = (id: string): (() => void) => {
|
||||||
return () => {
|
return () => {
|
||||||
let req = new DeleteApiKeyRequest();
|
let req = new DeleteApiKeyRequest();
|
||||||
req.setId(id);
|
req.setId(id);
|
||||||
|
|
||||||
InternalStore.deleteApiKey(req, () => {
|
InternalStore.deleteApiKey(req, () => {
|
||||||
// trigger a data-table reload
|
// trigger a data-table reload
|
||||||
this.setState({
|
setRefreshKey(refreshKey + 1);
|
||||||
refreshKey: this.state.refreshKey + 1,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
const getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||||
let req = new ListApiKeysRequest();
|
let req = new ListApiKeysRequest();
|
||||||
req.setLimit(limit);
|
req.setLimit(limit);
|
||||||
req.setOffset(offset);
|
req.setOffset(offset);
|
||||||
req.setTenantId(this.props.tenant.getId());
|
req.setTenantId(props.tenant.getId());
|
||||||
|
|
||||||
InternalStore.listApiKeys(req, (resp: ListApiKeysResponse) => {
|
InternalStore.listApiKeys(req, (resp: ListApiKeysResponse) => {
|
||||||
const obj = resp.toObject();
|
const obj = resp.toObject();
|
||||||
@ -90,7 +77,6 @@ class ListTenantApiKeys extends Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
@ -101,7 +87,7 @@ class ListTenantApiKeys extends Component<IProps, IState> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -111,17 +97,16 @@ class ListTenantApiKeys extends Component<IProps, IState> {
|
|||||||
)}
|
)}
|
||||||
title="API keys"
|
title="API keys"
|
||||||
extra={[
|
extra={[
|
||||||
<Admin tenantId={this.props.tenant.getId()} isTenantAdmin>
|
<Admin tenantId={props.tenant.getId()} isTenantAdmin>
|
||||||
<Button type="primary">
|
<Button type="primary">
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/api-keys/create`}>Add API key</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/api-keys/create`}>Add API key</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</Admin>,
|
</Admin>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" refreshKey={this.state.refreshKey} />
|
<DataTable columns={columns} getPage={getPage} rowKey="id" refreshKey={refreshKey} />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListTenantApiKeys;
|
export default ListTenantApiKeys;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
import { Form, Input, Button } from "antd";
|
import { Form, Input, Button } from "antd";
|
||||||
|
|
||||||
@ -9,9 +7,9 @@ interface IProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApplicationForm extends Component<IProps> {
|
function ApplicationForm(props: IProps) {
|
||||||
onFinish = (values: Application.AsObject) => {
|
const onFinish = (values: Application.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let app = new Application();
|
let app = new Application();
|
||||||
|
|
||||||
app.setId(v.id);
|
app.setId(v.id);
|
||||||
@ -19,26 +17,24 @@ class ApplicationForm extends Component<IProps> {
|
|||||||
app.setName(v.name);
|
app.setName(v.name);
|
||||||
app.setDescription(v.description);
|
app.setDescription(v.description);
|
||||||
|
|
||||||
this.props.onFinish(app);
|
props.onFinish(app);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
||||||
<Input disabled={this.props.disabled} />
|
<Input disabled={props.disabled} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Description" name="description">
|
<Form.Item label="Description" name="description">
|
||||||
<Input.TextArea disabled={this.props.disabled} />
|
<Input.TextArea disabled={props.disabled} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" htmlType="submit" disabled={this.props.disabled}>
|
<Button type="primary" htmlType="submit" disabled={props.disabled}>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ApplicationForm;
|
export default ApplicationForm;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import { Route, Routes, Link, useNavigate, useLocation } from "react-router-dom";
|
||||||
import { Route, Switch, RouteComponentProps, Link } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, Button, PageHeader, Menu } from "antd";
|
import { Space, Breadcrumb, Card, Button, Menu } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import { Application, DeleteApplicationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { Application, DeleteApplicationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -38,31 +38,33 @@ import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate";
|
|||||||
import CreateIftttIntegration from "./integrations/CreateIftttIntegration";
|
import CreateIftttIntegration from "./integrations/CreateIftttIntegration";
|
||||||
import EditIftttIntegration from "./integrations/EditIftttIntegration";
|
import EditIftttIntegration from "./integrations/EditIftttIntegration";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
application: Application;
|
application: Application;
|
||||||
measurementKeys: string[];
|
measurementKeys: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApplicationLayout extends Component<IProps> {
|
function ApplicationLayout(props: IProps) {
|
||||||
deleteApplication = () => {
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const deleteApplication = () => {
|
||||||
let req = new DeleteApplicationRequest();
|
let req = new DeleteApplicationRequest();
|
||||||
req.setId(this.props.application.getId());
|
req.setId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.delete(req, () => {
|
ApplicationStore.delete(req, () => {
|
||||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications`);
|
navigate(`/tenants/${props.tenant.getId()}/applications`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const tenant = props.tenant;
|
||||||
const tenant = this.props.tenant;
|
const app = props.application;
|
||||||
const app = this.props.application;
|
|
||||||
|
|
||||||
if (!app) {
|
if (!app) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = this.props.history.location.pathname;
|
const path = location.pathname;
|
||||||
let tab = "devices";
|
let tab = "devices";
|
||||||
|
|
||||||
if (path.endsWith("/multicast-groups")) {
|
if (path.endsWith("/multicast-groups")) {
|
||||||
@ -93,12 +95,12 @@ class ApplicationLayout extends Component<IProps> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/applications`}>Applications</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -109,8 +111,8 @@ class ApplicationLayout extends Component<IProps> {
|
|||||||
title={app.getName()}
|
title={app.getName()}
|
||||||
subTitle={`application id: ${app.getId()}`}
|
subTitle={`application id: ${app.getId()}`}
|
||||||
extra={[
|
extra={[
|
||||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
<Admin tenantId={props.tenant.getId()} isDeviceAdmin>
|
||||||
<DeleteConfirm confirm={app.getName()} typ="application" onConfirm={this.deleteApplication}>
|
<DeleteConfirm confirm={app.getName()} typ="application" onConfirm={deleteApplication}>
|
||||||
<Button danger type="primary">
|
<Button danger type="primary">
|
||||||
Delete application
|
Delete application
|
||||||
</Button>
|
</Button>
|
||||||
@ -124,14 +126,10 @@ class ApplicationLayout extends Component<IProps> {
|
|||||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}`}>Devices</Link>
|
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}`}>Devices</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="mg">
|
<Menu.Item key="mg">
|
||||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/multicast-groups`}>
|
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/multicast-groups`}>Multicast groups</Link>
|
||||||
Multicast groups
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="relay">
|
<Menu.Item key="relay">
|
||||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/relays`}>
|
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/relays`}>Relays</Link>
|
||||||
Relays
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="edit">
|
<Menu.Item key="edit">
|
||||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/edit`}>Application configuration</Link>
|
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/edit`}>Application configuration</Link>
|
||||||
@ -142,143 +140,56 @@ class ApplicationLayout extends Component<IProps> {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
<Switch>
|
<Routes>
|
||||||
<Route exact path={this.props.match.path} render={props => <ListDevices application={app} {...props} />} />
|
<Route path="/" element={<ListDevices application={app} />} />
|
||||||
<Route
|
<Route path="/edit" element={<EditApplication application={app} />} />
|
||||||
exact
|
<Route path="/integrations" element={<ListIntegrations application={app} />} />
|
||||||
path={`${this.props.match.path}/edit`}
|
<Route path="/multicast-groups" element={<ListMulticastGroups application={app} />} />
|
||||||
render={props => <EditApplication application={app} {...props} />}
|
<Route path="/relays" element={<ListRelays application={app} />} />
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations`}
|
|
||||||
render={props => <ListIntegrations application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/multicast-groups`}
|
|
||||||
render={props => <ListMulticastGroups application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/relays`}
|
|
||||||
render={props => <ListRelays application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
<Route path="/integrations/http/create" element={<CreateHttpIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/http/edit" element={<EditHttpIntegration application={app} />} />
|
||||||
|
|
||||||
|
<Route path="/integrations/http/create" element={<CreateHttpIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/http/edit" element={<EditHttpIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/aws-sns/create" element={<CreateAwsSnsIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/aws-sns/edit" element={<EditAwsSnsIntegration application={app} />} />
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/integrations/azure-service-bus/create"
|
||||||
path={`${this.props.match.path}/integrations/http/create`}
|
element={<CreateAzureServiceBusIntegration application={app} />}
|
||||||
render={props => <CreateHttpIntegration application={app} {...props} />}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/integrations/azure-service-bus/edit"
|
||||||
path={`${this.props.match.path}/integrations/http/edit`}
|
element={<EditAzureServiceBusIntegration application={app} />}
|
||||||
render={props => <EditHttpIntegration application={app} {...props} />}
|
/>
|
||||||
|
<Route path="/integrations/gcp-pub-sub/create" element={<CreateGcpPubSubIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/gcp-pub-sub/edit" element={<EditGcpPubSubIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/influxdb/create" element={<CreateInfluxDbIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/influxdb/edit" element={<EditInfluxDbIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/mydevices/create" element={<CreateMyDevicesIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/mydevices/edit" element={<EditMyDevicesIntegration application={app} />} />
|
||||||
|
<Route
|
||||||
|
path="/integrations/pilot-things/create"
|
||||||
|
element={<CreatePilotThingsIntegration application={app} />}
|
||||||
|
/>
|
||||||
|
<Route path="/integrations/pilot-things/edit" element={<EditPilotThingsIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/loracloud/create" element={<CreateLoRaCloudIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/loracloud/edit" element={<EditLoRaCloudIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/thingsboard/create" element={<CreateThingsBoardIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/thingsboard/edit" element={<EditThingsBoardIntegration application={app} />} />
|
||||||
|
<Route path="/integrations/mqtt/certificate" element={<GenerateMqttCertificate application={app} />} />
|
||||||
|
<Route
|
||||||
|
path="/integrations/ifttt/create"
|
||||||
|
element={<CreateIftttIntegration application={app} measurementKeys={props.measurementKeys} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/integrations/ifttt/edit"
|
||||||
path={`${this.props.match.path}/integrations/aws-sns/create`}
|
element={<EditIftttIntegration application={app} measurementKeys={props.measurementKeys} />}
|
||||||
render={props => <CreateAwsSnsIntegration application={app} {...props} />}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
</Routes>
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/aws-sns/edit`}
|
|
||||||
render={props => <EditAwsSnsIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/azure-service-bus/create`}
|
|
||||||
render={props => <CreateAzureServiceBusIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/azure-service-bus/edit`}
|
|
||||||
render={props => <EditAzureServiceBusIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/gcp-pub-sub/create`}
|
|
||||||
render={props => <CreateGcpPubSubIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/gcp-pub-sub/edit`}
|
|
||||||
render={props => <EditGcpPubSubIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/influxdb/create`}
|
|
||||||
render={props => <CreateInfluxDbIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/influxdb/edit`}
|
|
||||||
render={props => <EditInfluxDbIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/mydevices/create`}
|
|
||||||
render={props => <CreateMyDevicesIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/mydevices/edit`}
|
|
||||||
render={props => <EditMyDevicesIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/pilot-things/create`}
|
|
||||||
render={props => <CreatePilotThingsIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/pilot-things/edit`}
|
|
||||||
render={props => <EditPilotThingsIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/loracloud/create`}
|
|
||||||
render={props => <CreateLoRaCloudIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/loracloud/edit`}
|
|
||||||
render={props => <EditLoRaCloudIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/thingsboard/create`}
|
|
||||||
render={props => <CreateThingsBoardIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/thingsboard/edit`}
|
|
||||||
render={props => <EditThingsBoardIntegration application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/mqtt/certificate`}
|
|
||||||
render={props => <GenerateMqttCertificate application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/ifttt/create`}
|
|
||||||
render={props => (
|
|
||||||
<CreateIftttIntegration application={app} measurementKeys={this.props.measurementKeys} {...props} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/integrations/ifttt/edit`}
|
|
||||||
render={props => (
|
|
||||||
<EditIftttIntegration application={app} measurementKeys={this.props.measurementKeys} {...props} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Switch>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ApplicationLayout;
|
export default ApplicationLayout;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Route, Switch, RouteComponentProps } from "react-router-dom";
|
import { Route, Routes, useParams } from "react-router-dom";
|
||||||
|
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import {
|
import {
|
||||||
@ -16,90 +16,57 @@ import MulticastGroupLayout from "../multicast-groups/MulticastGroupLayout";
|
|||||||
import CreateMulticastGroup from "../multicast-groups/CreateMulticastGroup";
|
import CreateMulticastGroup from "../multicast-groups/CreateMulticastGroup";
|
||||||
import RelayLayout from "../relays/RelayLayout";
|
import RelayLayout from "../relays/RelayLayout";
|
||||||
|
|
||||||
interface MatchParams {
|
interface IProps {
|
||||||
applicationId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps<MatchParams> {
|
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function ApplicationLoader(props: IProps) {
|
||||||
application?: Application;
|
const { applicationId } = useParams();
|
||||||
measurementKeys: string[];
|
const [application, setApplication] = useState<Application | undefined>(undefined);
|
||||||
}
|
const [measurementKeys, setMeasurementKeys] = useState<string[]>([]);
|
||||||
|
|
||||||
class ApplicationLoader extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
ApplicationStore.on("change", loadApplication);
|
||||||
super(props);
|
loadApplication();
|
||||||
this.state = {
|
|
||||||
measurementKeys: [],
|
return () => {
|
||||||
|
ApplicationStore.removeAllListeners("change");
|
||||||
};
|
};
|
||||||
}
|
}, [applicationId]);
|
||||||
|
|
||||||
componentDidMount() {
|
const loadApplication = () => {
|
||||||
this.getApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
getApplication = () => {
|
|
||||||
let req = new GetApplicationRequest();
|
let req = new GetApplicationRequest();
|
||||||
req.setId(this.props.match.params.applicationId);
|
req.setId(applicationId!);
|
||||||
|
|
||||||
ApplicationStore.get(req, (resp: GetApplicationResponse) => {
|
ApplicationStore.get(req, (resp: GetApplicationResponse) => {
|
||||||
this.setState({
|
setApplication(resp.getApplication());
|
||||||
application: resp.getApplication(),
|
setMeasurementKeys(resp.getMeasurementKeysList());
|
||||||
measurementKeys: resp.getMeasurementKeysList(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const app = application;
|
||||||
const app = this.state.application;
|
|
||||||
if (!app) {
|
if (!app) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = this.props.match.path;
|
const tenant = props.tenant;
|
||||||
const tenant = this.props.tenant;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Routes>
|
||||||
|
<Route path="/devices/create" element={<CreateDevice tenant={tenant} application={app} />} />
|
||||||
|
<Route path="/multicast-groups/create" element={<CreateMulticastGroup tenant={tenant} application={app} />} />
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/multicast-groups/:multicastGroupId/*"
|
||||||
path={`${path}/devices/create`}
|
element={<MulticastGroupLayout tenant={tenant} application={app} />}
|
||||||
render={props => <CreateDevice tenant={tenant} application={app} {...props} />}
|
|
||||||
/>
|
/>
|
||||||
|
<Route path="/devices/:devEui/*" element={<DeviceLayout tenant={tenant} application={app} />} />
|
||||||
|
<Route path="/relays/:relayDevEui/*" element={<RelayLayout tenant={tenant} application={app} />} />
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/*"
|
||||||
path={`${path}/multicast-groups/create`}
|
element={<ApplicationLayout tenant={tenant} application={app} measurementKeys={measurementKeys} />}
|
||||||
render={props => <CreateMulticastGroup tenant={tenant} application={app} {...props} />}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
</Routes>
|
||||||
path={`${path}/multicast-groups/:multicastGroupId([\\w-]{36})`}
|
|
||||||
render={(props: any) => <MulticastGroupLayout tenant={tenant} application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`${path}/devices/:devEui([0-9a-f]{16})`}
|
|
||||||
component={(props: any) => <DeviceLayout tenant={tenant} application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={`${path}/relays/:relayDevEui([0-9a-f]{16})`}
|
|
||||||
component={(props: any) => <RelayLayout tenant={tenant} application={app} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path={path}
|
|
||||||
render={props => (
|
|
||||||
<ApplicationLayout
|
|
||||||
tenant={tenant}
|
|
||||||
application={app}
|
|
||||||
measurementKeys={this.state.measurementKeys}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Switch>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ApplicationLoader;
|
export default ApplicationLoader;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { Link, RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
import { Space, Breadcrumb, Card } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import {
|
import {
|
||||||
@ -13,23 +13,24 @@ import {
|
|||||||
import ApplicationForm from "./ApplicationForm";
|
import ApplicationForm from "./ApplicationForm";
|
||||||
import ApplicationStore from "../../stores/ApplicationStore";
|
import ApplicationStore from "../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateApplication extends Component<IProps> {
|
function CreateApplication(props: IProps) {
|
||||||
onFinish = (obj: Application) => {
|
const navigate = useNavigate();
|
||||||
obj.setTenantId(this.props.tenant.getId());
|
|
||||||
|
const onFinish = (obj: Application) => {
|
||||||
|
obj.setTenantId(props.tenant.getId());
|
||||||
|
|
||||||
let req = new CreateApplicationRequest();
|
let req = new CreateApplicationRequest();
|
||||||
req.setApplication(obj);
|
req.setApplication(obj);
|
||||||
|
|
||||||
ApplicationStore.create(req, (resp: CreateApplicationResponse) => {
|
ApplicationStore.create(req, (resp: CreateApplicationResponse) => {
|
||||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${resp.getId()}`);
|
navigate(`/tenants/${props.tenant.getId()}/applications/${resp.getId()}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const app = new Application();
|
const app = new Application();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -42,12 +43,12 @@ class CreateApplication extends Component<IProps> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/applications`}>Applications</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -58,11 +59,10 @@ class CreateApplication extends Component<IProps> {
|
|||||||
title="Add application"
|
title="Add application"
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<ApplicationForm initialValues={app} onFinish={this.onFinish} />
|
<ApplicationForm initialValues={app} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateApplication;
|
export default CreateApplication;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Application, UpdateApplicationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { Application, UpdateApplicationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
|
|
||||||
@ -7,31 +6,29 @@ import ApplicationStore from "../../stores/ApplicationStore";
|
|||||||
import ApplicationForm from "./ApplicationForm";
|
import ApplicationForm from "./ApplicationForm";
|
||||||
import SessionStore from "../../stores/SessionStore";
|
import SessionStore from "../../stores/SessionStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditApplication extends Component<IProps> {
|
function EditApplication(props: IProps) {
|
||||||
onFinish = (obj: Application) => {
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const onFinish = (obj: Application) => {
|
||||||
let req = new UpdateApplicationRequest();
|
let req = new UpdateApplicationRequest();
|
||||||
req.setApplication(obj);
|
req.setApplication(obj);
|
||||||
|
|
||||||
ApplicationStore.update(req, () => {
|
ApplicationStore.update(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const disabled = !(
|
const disabled = !(
|
||||||
SessionStore.isAdmin() ||
|
SessionStore.isAdmin() ||
|
||||||
SessionStore.isTenantAdmin(this.props.application.getTenantId()) ||
|
SessionStore.isTenantAdmin(props.application.getTenantId()) ||
|
||||||
SessionStore.isTenantDeviceAdmin(this.props.application.getTenantId())
|
SessionStore.isTenantDeviceAdmin(props.application.getTenantId())
|
||||||
);
|
);
|
||||||
|
|
||||||
return <ApplicationForm initialValues={this.props.application} disabled={disabled} onFinish={this.onFinish} />;
|
return <ApplicationForm initialValues={props.application} disabled={disabled} onFinish={onFinish} />;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditApplication;
|
export default EditApplication;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
import { Space, Breadcrumb, Button } from "antd";
|
||||||
import { ColumnsType } from "antd/es/table";
|
import { ColumnsType } from "antd/es/table";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ListApplicationsRequest,
|
ListApplicationsRequest,
|
||||||
@ -19,17 +19,14 @@ interface IProps {
|
|||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListApplications extends Component<IProps> {
|
function ListApplications(props: IProps) {
|
||||||
columns = (): ColumnsType<ApplicationListItem.AsObject> => {
|
const columns: ColumnsType<ApplicationListItem.AsObject> = [
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
title: "Name",
|
title: "Name",
|
||||||
dataIndex: "name",
|
dataIndex: "name",
|
||||||
key: "name",
|
key: "name",
|
||||||
width: 250,
|
width: 250,
|
||||||
render: (text, record) => (
|
render: (text, record) => <Link to={`/tenants/${props.tenant.getId()}/applications/${record.id}`}>{text}</Link>,
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/${record.id}`}>{text}</Link>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Description",
|
title: "Description",
|
||||||
@ -37,11 +34,10 @@ class ListApplications extends Component<IProps> {
|
|||||||
key: "description",
|
key: "description",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
const getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||||
let req = new ListApplicationsRequest();
|
let req = new ListApplicationsRequest();
|
||||||
req.setTenantId(this.props.tenant.getId());
|
req.setTenantId(props.tenant.getId());
|
||||||
req.setLimit(limit);
|
req.setLimit(limit);
|
||||||
req.setOffset(offset);
|
req.setOffset(offset);
|
||||||
|
|
||||||
@ -51,7 +47,6 @@ class ListApplications extends Component<IProps> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
@ -62,7 +57,7 @@ class ListApplications extends Component<IProps> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -72,17 +67,16 @@ class ListApplications extends Component<IProps> {
|
|||||||
)}
|
)}
|
||||||
title="Applications"
|
title="Applications"
|
||||||
extra={[
|
extra={[
|
||||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
<Admin tenantId={props.tenant.getId()} isDeviceAdmin>
|
||||||
<Button type="primary">
|
<Button type="primary">
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/create`}>Add application</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/applications/create`}>Add application</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</Admin>,
|
</Admin>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" />
|
<DataTable columns={columns} getPage={getPage} rowKey="id" />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListApplications;
|
export default ListApplications;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { Row } from "antd";
|
import { Row } from "antd";
|
||||||
|
|
||||||
@ -27,32 +27,22 @@ interface IProps {
|
|||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function ListIntegrations(props: IProps) {
|
||||||
configured: any[];
|
const [configured, setConfigured] = useState<any[]>([]);
|
||||||
available: any[];
|
const [available, setAvailable] = useState<any[]>([]);
|
||||||
}
|
|
||||||
|
|
||||||
class ListIntegrations extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
ApplicationStore.on("integration.delete", loadIntegrations);
|
||||||
super(props);
|
loadIntegrations();
|
||||||
this.state = {
|
|
||||||
configured: [],
|
|
||||||
available: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
return () => {
|
||||||
ApplicationStore.on("integration.delete", this.loadIntegrations);
|
|
||||||
this.loadIntegrations();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
ApplicationStore.removeAllListeners("integration.delete");
|
ApplicationStore.removeAllListeners("integration.delete");
|
||||||
}
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
loadIntegrations = () => {
|
const loadIntegrations = () => {
|
||||||
let req = new ListIntegrationsRequest();
|
let req = new ListIntegrationsRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.listIntegrations(req, (resp: ListIntegrationsResponse) => {
|
ApplicationStore.listIntegrations(req, (resp: ListIntegrationsResponse) => {
|
||||||
let configured: any[] = [];
|
let configured: any[] = [];
|
||||||
@ -70,94 +60,90 @@ class ListIntegrations extends Component<IProps, IState> {
|
|||||||
|
|
||||||
// AWS SNS
|
// AWS SNS
|
||||||
if (includes(resp.getResultList(), IntegrationKind.AWS_SNS)) {
|
if (includes(resp.getResultList(), IntegrationKind.AWS_SNS)) {
|
||||||
configured.push(<AwsSnsCard application={this.props.application} />);
|
configured.push(<AwsSnsCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<AwsSnsCard application={this.props.application} add />);
|
available.push(<AwsSnsCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Azure Service-Bus
|
// Azure Service-Bus
|
||||||
if (includes(resp.getResultList(), IntegrationKind.AZURE_SERVICE_BUS)) {
|
if (includes(resp.getResultList(), IntegrationKind.AZURE_SERVICE_BUS)) {
|
||||||
configured.push(<AzureServiceBusCard application={this.props.application} />);
|
configured.push(<AzureServiceBusCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<AzureServiceBusCard application={this.props.application} add />);
|
available.push(<AzureServiceBusCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GCP Pub/Sub
|
// GCP Pub/Sub
|
||||||
if (includes(resp.getResultList(), IntegrationKind.GCP_PUB_SUB)) {
|
if (includes(resp.getResultList(), IntegrationKind.GCP_PUB_SUB)) {
|
||||||
configured.push(<GcpPubSubCard application={this.props.application} />);
|
configured.push(<GcpPubSubCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<GcpPubSubCard application={this.props.application} add />);
|
available.push(<GcpPubSubCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
if (includes(resp.getResultList(), IntegrationKind.HTTP)) {
|
if (includes(resp.getResultList(), IntegrationKind.HTTP)) {
|
||||||
configured.push(<HttpCard application={this.props.application} />);
|
configured.push(<HttpCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<HttpCard application={this.props.application} add />);
|
available.push(<HttpCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IFTTT
|
// IFTTT
|
||||||
if (includes(resp.getResultList(), IntegrationKind.IFTTT)) {
|
if (includes(resp.getResultList(), IntegrationKind.IFTTT)) {
|
||||||
configured.push(<IftttCard application={this.props.application} />);
|
configured.push(<IftttCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<IftttCard application={this.props.application} add />);
|
available.push(<IftttCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfluxDB
|
// InfluxDB
|
||||||
if (includes(resp.getResultList(), IntegrationKind.INFLUX_DB)) {
|
if (includes(resp.getResultList(), IntegrationKind.INFLUX_DB)) {
|
||||||
configured.push(<InfluxdbCard application={this.props.application} />);
|
configured.push(<InfluxdbCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<InfluxdbCard application={this.props.application} add />);
|
available.push(<InfluxdbCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
if (includes(resp.getResultList(), IntegrationKind.MQTT_GLOBAL)) {
|
if (includes(resp.getResultList(), IntegrationKind.MQTT_GLOBAL)) {
|
||||||
configured.push(<MqttCard application={this.props.application} />);
|
configured.push(<MqttCard application={props.application} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// myDevices
|
// myDevices
|
||||||
if (includes(resp.getResultList(), IntegrationKind.MY_DEVICES)) {
|
if (includes(resp.getResultList(), IntegrationKind.MY_DEVICES)) {
|
||||||
configured.push(<MyDevicesCard application={this.props.application} />);
|
configured.push(<MyDevicesCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<MyDevicesCard application={this.props.application} add />);
|
available.push(<MyDevicesCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pilot Things
|
// Pilot Things
|
||||||
if (includes(resp.getResultList(), IntegrationKind.PILOT_THINGS)) {
|
if (includes(resp.getResultList(), IntegrationKind.PILOT_THINGS)) {
|
||||||
configured.push(<PilotThingsCard application={this.props.application} />);
|
configured.push(<PilotThingsCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<PilotThingsCard application={this.props.application} add />);
|
available.push(<PilotThingsCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Semtech LoRa Cloud
|
// Semtech LoRa Cloud
|
||||||
if (includes(resp.getResultList(), IntegrationKind.LORA_CLOUD)) {
|
if (includes(resp.getResultList(), IntegrationKind.LORA_CLOUD)) {
|
||||||
configured.push(<LoRaCloudCard application={this.props.application} />);
|
configured.push(<LoRaCloudCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<LoRaCloudCard application={this.props.application} add />);
|
available.push(<LoRaCloudCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThingsBoard
|
// ThingsBoard
|
||||||
if (includes(resp.getResultList(), IntegrationKind.THINGS_BOARD)) {
|
if (includes(resp.getResultList(), IntegrationKind.THINGS_BOARD)) {
|
||||||
configured.push(<ThingsBoardCard application={this.props.application} />);
|
configured.push(<ThingsBoardCard application={props.application} />);
|
||||||
} else {
|
} else {
|
||||||
available.push(<ThingsBoardCard application={this.props.application} add />);
|
available.push(<ThingsBoardCard application={props.application} add />);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setConfigured(configured);
|
||||||
configured: configured,
|
setAvailable(available);
|
||||||
available: available,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
{this.state.configured}
|
{configured}
|
||||||
{this.state.available}
|
{available}
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListIntegrations;
|
export default ListIntegrations;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -13,29 +12,28 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AwsSns extends Component<IProps> {
|
function AwsSns(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteAwsSnsIntegrationRequest();
|
let req = new DeleteAwsSnsIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.deleteAwsSnsIntegration(req, () => {});
|
ApplicationStore.deleteAwsSnsIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/aws-sns/create">
|
<Link to="aws-sns/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/aws-sns/edit">
|
<Link to="aws-sns/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -54,6 +52,5 @@ class AwsSns extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default AwsSns;
|
export default AwsSns;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Button, Select } from "antd";
|
import { Form, Input, Button, Select } from "antd";
|
||||||
|
|
||||||
import { AwsSnsIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { AwsSnsIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -9,9 +7,9 @@ interface IProps {
|
|||||||
onFinish: (obj: AwsSnsIntegration) => void;
|
onFinish: (obj: AwsSnsIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AwsSnsIntegrationForm extends Component<IProps> {
|
function AwsSnsIntegrationForm(props: IProps) {
|
||||||
onFinish = (values: AwsSnsIntegration.AsObject) => {
|
const onFinish = (values: AwsSnsIntegration.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let i = new AwsSnsIntegration();
|
let i = new AwsSnsIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
@ -21,12 +19,11 @@ class AwsSnsIntegrationForm extends Component<IProps> {
|
|||||||
i.setSecretAccessKey(v.secretAccessKey);
|
i.setSecretAccessKey(v.secretAccessKey);
|
||||||
i.setTopicArn(v.topicArn);
|
i.setTopicArn(v.topicArn);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Payload encoding"
|
label="Payload encoding"
|
||||||
name="encoding"
|
name="encoding"
|
||||||
@ -69,6 +66,5 @@ class AwsSnsIntegrationForm extends Component<IProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default AwsSnsIntegrationForm;
|
export default AwsSnsIntegrationForm;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -16,28 +15,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AzureServiceBusCard extends Component<IProps> {
|
function AzureServiceBusCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteAzureServiceBusIntegrationRequest();
|
let req = new DeleteAzureServiceBusIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteAzureServiceBusIntegration(req, () => {});
|
ApplicationStore.deleteAzureServiceBusIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/azure-service-bus/create">
|
<Link to="azure-service-bus/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/azure-service-bus/edit">
|
<Link to="azure-service-bus/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -56,6 +54,5 @@ class AzureServiceBusCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default AzureServiceBusCard;
|
export default AzureServiceBusCard;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Button, Select } from "antd";
|
import { Form, Input, Button, Select } from "antd";
|
||||||
|
|
||||||
import { AzureServiceBusIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { AzureServiceBusIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -9,9 +7,9 @@ interface IProps {
|
|||||||
onFinish: (obj: AzureServiceBusIntegration) => void;
|
onFinish: (obj: AzureServiceBusIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AzureServiceBusIntegrationForm extends Component<IProps> {
|
function AzureServiceBusIntegrationForm(props: IProps) {
|
||||||
onFinish = (values: AzureServiceBusIntegration.AsObject) => {
|
const onFinish = (values: AzureServiceBusIntegration.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let i = new AzureServiceBusIntegration();
|
let i = new AzureServiceBusIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
@ -19,12 +17,11 @@ class AzureServiceBusIntegrationForm extends Component<IProps> {
|
|||||||
i.setConnectionString(v.connectionString);
|
i.setConnectionString(v.connectionString);
|
||||||
i.setPublishName(v.publishName);
|
i.setPublishName(v.publishName);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Payload encoding"
|
label="Payload encoding"
|
||||||
name="encoding"
|
name="encoding"
|
||||||
@ -39,14 +36,24 @@ class AzureServiceBusIntegrationForm extends Component<IProps> {
|
|||||||
label="Azure Service-Bus connection string"
|
label="Azure Service-Bus connection string"
|
||||||
name="connectionString"
|
name="connectionString"
|
||||||
tooltip="This string can be obtained after creating a 'Shared access policy' with 'Send' permission."
|
tooltip="This string can be obtained after creating a 'Shared access policy' with 'Send' permission."
|
||||||
rules={[{ required: true, message: "Please enter an Azure Service-Bus connection string!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter an Azure Service-Bus connection string!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Azure Service-Bus topic / queue name"
|
label="Azure Service-Bus topic / queue name"
|
||||||
name="publishName"
|
name="publishName"
|
||||||
rules={[{ required: true, message: "Please enter an Azure Service-Bus topic / queue name!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter an Azure Service-Bus topic / queue name!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -58,6 +65,5 @@ class AzureServiceBusIntegrationForm extends Component<IProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default AzureServiceBusIntegrationForm;
|
export default AzureServiceBusIntegrationForm;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import AwsSnsIntegrationForm from "./AwsSnsIntegrationForm";
|
import AwsSnsIntegrationForm from "./AwsSnsIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateAwsSnsIntegration extends Component<IProps> {
|
function CreateAwsSnsIntegration(props: IProps) {
|
||||||
onFinish = (obj: AwsSnsIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: AwsSnsIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateAwsSnsIntegrationRequest();
|
let req = new CreateAwsSnsIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createAwsSnsIntegration(req, () => {
|
ApplicationStore.createAwsSnsIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new AwsSnsIntegration();
|
const i = new AwsSnsIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add AWS SNS integration">
|
<Card title="Add AWS SNS integration">
|
||||||
<AwsSnsIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<AwsSnsIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateAwsSnsIntegration;
|
export default CreateAwsSnsIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
|
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateAzureServiceBusIntegration extends Component<IProps> {
|
function CreateAzureServiceBusIntegration(props: IProps) {
|
||||||
onFinish = (obj: AzureServiceBusIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: AzureServiceBusIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateAzureServiceBusIntegrationRequest();
|
let req = new CreateAzureServiceBusIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createAzureServiceBusIntegration(req, () => {
|
ApplicationStore.createAzureServiceBusIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new AzureServiceBusIntegration();
|
const i = new AzureServiceBusIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add Azure Service-Bus integration">
|
<Card title="Add Azure Service-Bus integration">
|
||||||
<AzureServiceBusIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<AzureServiceBusIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateAzureServiceBusIntegration;
|
export default CreateAzureServiceBusIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
|
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateGcpPubSubIntegration extends Component<IProps> {
|
function CreateGcpPubSubIntegration(props: IProps) {
|
||||||
onFinish = (obj: GcpPubSubIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: GcpPubSubIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateGcpPubSubIntegrationRequest();
|
let req = new CreateGcpPubSubIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createGcpPubSubIntegration(req, () => {
|
ApplicationStore.createGcpPubSubIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new GcpPubSubIntegration();
|
const i = new GcpPubSubIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add GCP Pub/Sub integration">
|
<Card title="Add GCP Pub/Sub integration">
|
||||||
<GcpPubSubIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<GcpPubSubIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateGcpPubSubIntegration;
|
export default CreateGcpPubSubIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import HttpIntegrationForm from "./HttpIntegrationForm";
|
import HttpIntegrationForm from "./HttpIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateHttpIntegration extends Component<IProps> {
|
function CreateHttpIntegration(props: IProps) {
|
||||||
onFinish = (obj: HttpIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: HttpIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateHttpIntegrationRequest();
|
let req = new CreateHttpIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createHttpIntegration(req, () => {
|
ApplicationStore.createHttpIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new HttpIntegration();
|
const i = new HttpIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add HTTP integration">
|
<Card title="Add HTTP integration">
|
||||||
<HttpIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<HttpIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateHttpIntegration;
|
export default CreateHttpIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,35 +11,33 @@ import {
|
|||||||
import IftttIntegrationForm from "./IftttIntegrationForm";
|
import IftttIntegrationForm from "./IftttIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
measurementKeys: string[];
|
measurementKeys: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateIftttIntegration extends Component<IProps> {
|
function CreateIftttIntegration(props: IProps) {
|
||||||
onFinish = (obj: IftttIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: IftttIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateIftttIntegrationRequest();
|
let req = new CreateIftttIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createIftttIntegration(req, () => {
|
ApplicationStore.createIftttIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new IftttIntegration();
|
const i = new IftttIntegration();
|
||||||
i.setUplinkValuesList(["", ""]);
|
i.setUplinkValuesList(["", ""]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add IFTTT integration">
|
<Card title="Add IFTTT integration">
|
||||||
<IftttIntegrationForm measurementKeys={this.props.measurementKeys} initialValues={i} onFinish={this.onFinish} />
|
<IftttIntegrationForm measurementKeys={props.measurementKeys} initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateIftttIntegration;
|
export default CreateIftttIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
|
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateInfluxDbIntegration extends Component<IProps> {
|
function CreateInfluxDbIntegration(props: IProps) {
|
||||||
onFinish = (obj: InfluxDbIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: InfluxDbIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateInfluxDbIntegrationRequest();
|
let req = new CreateInfluxDbIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createInfluxDbIntegration(req, () => {
|
ApplicationStore.createInfluxDbIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new InfluxDbIntegration();
|
const i = new InfluxDbIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add InfluxDB integration">
|
<Card title="Add InfluxDB integration">
|
||||||
<InfluxDbIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<InfluxDbIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateInfluxDbIntegration;
|
export default CreateInfluxDbIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -13,25 +12,24 @@ import {
|
|||||||
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
|
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateLoRaCloudIntegration extends Component<IProps> {
|
function CreateLoRaCloudIntegration(props: IProps) {
|
||||||
onFinish = (obj: LoraCloudIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: LoraCloudIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateLoraCloudIntegrationRequest();
|
let req = new CreateLoraCloudIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createLoraCloudIntegration(req, () => {
|
ApplicationStore.createLoraCloudIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let i = new LoraCloudIntegration();
|
let i = new LoraCloudIntegration();
|
||||||
let mgs = new LoraCloudModemGeolocationServices();
|
let mgs = new LoraCloudModemGeolocationServices();
|
||||||
mgs.setModemEnabled(true);
|
mgs.setModemEnabled(true);
|
||||||
@ -41,10 +39,9 @@ class CreateLoRaCloudIntegration extends Component<IProps> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add Semtech LoRa Cloud™ integration">
|
<Card title="Add Semtech LoRa Cloud™ integration">
|
||||||
<LoRaCloudIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<LoRaCloudIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateLoRaCloudIntegration;
|
export default CreateLoRaCloudIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
|
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateMyDevicesIntegration extends Component<IProps> {
|
function CreateMyDevicesIntegration(props: IProps) {
|
||||||
onFinish = (obj: MyDevicesIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: MyDevicesIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateMyDevicesIntegrationRequest();
|
let req = new CreateMyDevicesIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createMyDevicesIntegration(req, () => {
|
ApplicationStore.createMyDevicesIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new MyDevicesIntegration();
|
const i = new MyDevicesIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add myDevices integration">
|
<Card title="Add myDevices integration">
|
||||||
<MyDevicesIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<MyDevicesIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateMyDevicesIntegration;
|
export default CreateMyDevicesIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
|
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreatePilotThingsIntegration extends Component<IProps> {
|
function CreatePilotThingsIntegration(props: IProps) {
|
||||||
onFinish = (obj: PilotThingsIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: PilotThingsIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreatePilotThingsIntegrationRequest();
|
let req = new CreatePilotThingsIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createPilotThingsIntegration(req, () => {
|
ApplicationStore.createPilotThingsIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new PilotThingsIntegration();
|
const i = new PilotThingsIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add Pilot Things integration">
|
<Card title="Add Pilot Things integration">
|
||||||
<PilotThingsIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<PilotThingsIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreatePilotThingsIntegration;
|
export default CreatePilotThingsIntegration;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -12,33 +11,31 @@ import {
|
|||||||
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
|
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateThingsBoardIntegration extends Component<IProps> {
|
function CreateThingsBoardIntegration(props: IProps) {
|
||||||
onFinish = (obj: ThingsBoardIntegration) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: ThingsBoardIntegration) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateThingsBoardIntegrationRequest();
|
let req = new CreateThingsBoardIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.createThingsBoardIntegration(req, () => {
|
ApplicationStore.createThingsBoardIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const i = new ThingsBoardIntegration();
|
const i = new ThingsBoardIntegration();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Add ThingsBoard integration">
|
<Card title="Add ThingsBoard integration">
|
||||||
<ThingsBoardIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
<ThingsBoardIntegrationForm initialValues={i} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateThingsBoardIntegration;
|
export default CreateThingsBoardIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import AwsSnsIntegrationForm from "./AwsSnsIntegrationForm";
|
import AwsSnsIntegrationForm from "./AwsSnsIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditAwsSnsIntegration(props: IProps) {
|
||||||
integration?: AwsSnsIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<AwsSnsIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditAwsSnsIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetAwsSnsIntegrationRequest();
|
let req = new GetAwsSnsIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getAwsSnsIntegration(req, (resp: GetAwsSnsIntegrationResponse) => {
|
ApplicationStore.getAwsSnsIntegration(req, (resp: GetAwsSnsIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: AwsSnsIntegration) => {
|
const onFinish = (obj: AwsSnsIntegration) => {
|
||||||
let req = new UpdateAwsSnsIntegrationRequest();
|
let req = new UpdateAwsSnsIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateAwsSnsIntegration(req, () => {
|
ApplicationStore.updateAwsSnsIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update AWS SNS integration">
|
<Card title="Update AWS SNS integration">
|
||||||
<AwsSnsIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<AwsSnsIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditAwsSnsIntegration;
|
export default EditAwsSnsIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
|
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditAzureServiceBusIntegration(props: IProps) {
|
||||||
integration?: AzureServiceBusIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<AzureServiceBusIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditAzureServiceBusIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetAzureServiceBusIntegrationRequest();
|
let req = new GetAzureServiceBusIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getAzureServiceBusIntegration(req, (resp: GetAzureServiceBusIntegrationResponse) => {
|
ApplicationStore.getAzureServiceBusIntegration(req, (resp: GetAzureServiceBusIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: AzureServiceBusIntegration) => {
|
const onFinish = (obj: AzureServiceBusIntegration) => {
|
||||||
let req = new UpdateAzureServiceBusIntegrationRequest();
|
let req = new UpdateAzureServiceBusIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateAzureServiceBusIntegration(req, () => {
|
ApplicationStore.updateAzureServiceBusIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update Azure Service-Bus integration">
|
<Card title="Update Azure Service-Bus integration">
|
||||||
<AzureServiceBusIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<AzureServiceBusIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditAzureServiceBusIntegration;
|
export default EditAzureServiceBusIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
|
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditGcpPubSubIntegration(props: IProps) {
|
||||||
integration?: GcpPubSubIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<GcpPubSubIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditGcpPubSubIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetGcpPubSubIntegrationRequest();
|
let req = new GetGcpPubSubIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getGcpPubSubIntegration(req, (resp: GetGcpPubSubIntegrationResponse) => {
|
ApplicationStore.getGcpPubSubIntegration(req, (resp: GetGcpPubSubIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: GcpPubSubIntegration) => {
|
const onFinish = (obj: GcpPubSubIntegration) => {
|
||||||
let req = new UpdateGcpPubSubIntegrationRequest();
|
let req = new UpdateGcpPubSubIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateGcpPubSubIntegration(req, () => {
|
ApplicationStore.updateGcpPubSubIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update GCP Pub/Sub integration">
|
<Card title="Update GCP Pub/Sub integration">
|
||||||
<GcpPubSubIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<GcpPubSubIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditGcpPubSubIntegration;
|
export default EditGcpPubSubIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import HttpIntegrationForm from "./HttpIntegrationForm";
|
import HttpIntegrationForm from "./HttpIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditHttpIntegration(props: IProps) {
|
||||||
integration?: HttpIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<HttpIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditHttpIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetHttpIntegrationRequest();
|
let req = new GetHttpIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getHttpIntegration(req, (resp: GetHttpIntegrationResponse) => {
|
ApplicationStore.getHttpIntegration(req, (resp: GetHttpIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: HttpIntegration) => {
|
const onFinish = (obj: HttpIntegration) => {
|
||||||
let req = new UpdateHttpIntegrationRequest();
|
let req = new UpdateHttpIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateHttpIntegration(req, () => {
|
ApplicationStore.updateHttpIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update HTTP integration">
|
<Card title="Update HTTP integration">
|
||||||
<HttpIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<HttpIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditHttpIntegration;
|
export default EditHttpIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,58 +14,42 @@ import {
|
|||||||
import IftttIntegrationForm from "./IftttIntegrationForm";
|
import IftttIntegrationForm from "./IftttIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
measurementKeys: string[];
|
measurementKeys: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditIftttIntegration(props: IProps) {
|
||||||
integration?: IftttIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<IftttIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditIftttIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetIftttIntegrationRequest();
|
let req = new GetIftttIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getIftttIntegration(req, (resp: GetIftttIntegrationResponse) => {
|
ApplicationStore.getIftttIntegration(req, (resp: GetIftttIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: IftttIntegration) => {
|
const onFinish = (obj: IftttIntegration) => {
|
||||||
let req = new UpdateIftttIntegrationRequest();
|
let req = new UpdateIftttIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateIftttIntegration(req, () => {
|
ApplicationStore.updateIftttIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update IFTTT integration">
|
<Card title="Update IFTTT integration">
|
||||||
<IftttIntegrationForm
|
<IftttIntegrationForm measurementKeys={props.measurementKeys} initialValues={integration} onFinish={onFinish} />
|
||||||
measurementKeys={this.props.measurementKeys}
|
|
||||||
initialValues={this.state.integration}
|
|
||||||
onFinish={this.onFinish}
|
|
||||||
/>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditIftttIntegration;
|
export default EditIftttIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
|
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditInfluxDbIntegration(props: IProps) {
|
||||||
integration?: InfluxDbIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<InfluxDbIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditInfluxDbIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetInfluxDbIntegrationRequest();
|
let req = new GetInfluxDbIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getInfluxDbIntegration(req, (resp: GetInfluxDbIntegrationResponse) => {
|
ApplicationStore.getInfluxDbIntegration(req, (resp: GetInfluxDbIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: InfluxDbIntegration) => {
|
const onFinish = (obj: InfluxDbIntegration) => {
|
||||||
let req = new UpdateInfluxDbIntegrationRequest();
|
let req = new UpdateInfluxDbIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateInfluxDbIntegration(req, () => {
|
ApplicationStore.updateInfluxDbIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update InfluxDB integration">
|
<Card title="Update InfluxDB integration">
|
||||||
<InfluxDbIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<InfluxDbIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditInfluxDbIntegration;
|
export default EditInfluxDbIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
|
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditLoRaCloudIntegration(props: IProps) {
|
||||||
integration?: LoraCloudIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<LoraCloudIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditLoRaCloudIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetLoraCloudIntegrationRequest();
|
let req = new GetLoraCloudIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getLoraCloudIntegration(req, (resp: GetLoraCloudIntegrationResponse) => {
|
ApplicationStore.getLoraCloudIntegration(req, (resp: GetLoraCloudIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: LoraCloudIntegration) => {
|
const onFinish = (obj: LoraCloudIntegration) => {
|
||||||
let req = new UpdateLoraCloudIntegrationRequest();
|
let req = new UpdateLoraCloudIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateLoraCloudIntegration(req, () => {
|
ApplicationStore.updateLoraCloudIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update Semtech LoRa Cloud™ integration">
|
<Card title="Update Semtech LoRa Cloud™ integration">
|
||||||
<LoRaCloudIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<LoRaCloudIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditLoRaCloudIntegration;
|
export default EditLoRaCloudIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
|
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditMyDevicesIntegration(props: IProps) {
|
||||||
integration?: MyDevicesIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<MyDevicesIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditMyDevicesIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetMyDevicesIntegrationRequest();
|
let req = new GetMyDevicesIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getMyDevicesIntegration(req, (resp: GetMyDevicesIntegrationResponse) => {
|
ApplicationStore.getMyDevicesIntegration(req, (resp: GetMyDevicesIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: MyDevicesIntegration) => {
|
const onFinish = (obj: MyDevicesIntegration) => {
|
||||||
let req = new UpdateMyDevicesIntegrationRequest();
|
let req = new UpdateMyDevicesIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateMyDevicesIntegration(req, () => {
|
ApplicationStore.updateMyDevicesIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update myDevices integration">
|
<Card title="Update myDevices integration">
|
||||||
<MyDevicesIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<MyDevicesIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditMyDevicesIntegration;
|
export default EditMyDevicesIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
|
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditPilotThingsIntegration(props: IProps) {
|
||||||
integration?: PilotThingsIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<PilotThingsIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditPilotThingsIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetPilotThingsIntegrationRequest();
|
let req = new GetPilotThingsIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getPilotThingsIntegration(req, (resp: GetPilotThingsIntegrationResponse) => {
|
ApplicationStore.getPilotThingsIntegration(req, (resp: GetPilotThingsIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: PilotThingsIntegration) => {
|
const onFinish = (obj: PilotThingsIntegration) => {
|
||||||
let req = new UpdatePilotThingsIntegrationRequest();
|
let req = new UpdatePilotThingsIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updatePilotThingsIntegration(req, () => {
|
ApplicationStore.updatePilotThingsIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update Pilot Things integration">
|
<Card title="Update Pilot Things integration">
|
||||||
<PilotThingsIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<PilotThingsIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditPilotThingsIntegration;
|
export default EditPilotThingsIntegration;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Card } from "antd";
|
import { Card } from "antd";
|
||||||
|
|
||||||
@ -14,53 +14,41 @@ import {
|
|||||||
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
|
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
|
||||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function EditThingsBoardIntegration(props: IProps) {
|
||||||
integration?: ThingsBoardIntegration;
|
const navigate = useNavigate();
|
||||||
}
|
const [integration, setIntegration] = useState<ThingsBoardIntegration | undefined>(undefined);
|
||||||
|
|
||||||
class EditThingsBoardIntegration extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetThingsBoardIntegrationRequest();
|
let req = new GetThingsBoardIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.getThingsBoardIntegration(req, (resp: GetThingsBoardIntegrationResponse) => {
|
ApplicationStore.getThingsBoardIntegration(req, (resp: GetThingsBoardIntegrationResponse) => {
|
||||||
this.setState({
|
setIntegration(resp.getIntegration());
|
||||||
integration: resp.getIntegration(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: ThingsBoardIntegration) => {
|
const onFinish = (obj: ThingsBoardIntegration) => {
|
||||||
let req = new UpdateThingsBoardIntegrationRequest();
|
let req = new UpdateThingsBoardIntegrationRequest();
|
||||||
req.setIntegration(obj);
|
req.setIntegration(obj);
|
||||||
|
|
||||||
ApplicationStore.updateThingsBoardIntegration(req, () => {
|
ApplicationStore.updateThingsBoardIntegration(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/integrations`);
|
||||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (integration === undefined) {
|
||||||
if (this.state.integration === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Update ThingsBoard integration">
|
<Card title="Update ThingsBoard integration">
|
||||||
<ThingsBoardIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
<ThingsBoardIntegrationForm initialValues={integration} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditThingsBoardIntegration;
|
export default EditThingsBoardIntegration;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -13,28 +12,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GcpPubSubCard extends Component<IProps> {
|
function GcpPubSubCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteGcpPubSubIntegrationRequest();
|
let req = new DeleteGcpPubSubIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteGcpPubSubIntegration(req, () => {});
|
ApplicationStore.deleteGcpPubSubIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/gcp-pub-sub/create">
|
<Link to="gcp-pub-sub/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/gcp-pub-sub/edit">
|
<Link to="gcp-pub-sub/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -53,6 +51,5 @@ class GcpPubSubCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default GcpPubSubCard;
|
export default GcpPubSubCard;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Button, Select } from "antd";
|
import { Form, Input, Button, Select } from "antd";
|
||||||
|
|
||||||
import { GcpPubSubIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { GcpPubSubIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -9,9 +7,9 @@ interface IProps {
|
|||||||
onFinish: (obj: GcpPubSubIntegration) => void;
|
onFinish: (obj: GcpPubSubIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GcpPubSubIntegrationForm extends Component<IProps> {
|
function GcpPubSubIntegrationForm(props: IProps) {
|
||||||
onFinish = (values: GcpPubSubIntegration.AsObject) => {
|
const onFinish = (values: GcpPubSubIntegration.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let i = new GcpPubSubIntegration();
|
let i = new GcpPubSubIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
@ -20,12 +18,11 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
|
|||||||
i.setTopicName(v.topicName);
|
i.setTopicName(v.topicName);
|
||||||
i.setCredentialsFile(v.credentialsFile);
|
i.setCredentialsFile(v.credentialsFile);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Payload encoding"
|
label="Payload encoding"
|
||||||
name="encoding"
|
name="encoding"
|
||||||
@ -54,7 +51,12 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
|
|||||||
label="GCP Service account credentials file"
|
label="GCP Service account credentials file"
|
||||||
name="credentialsFile"
|
name="credentialsFile"
|
||||||
tooltip="Under IAM create a Service account with 'Pub/Sub Publisher' role, then put the content of the JSON key in this field."
|
tooltip="Under IAM create a Service account with 'Pub/Sub Publisher' role, then put the content of the JSON key in this field."
|
||||||
rules={[{ required: true, message: "Please enter a GCP Service account credentials file!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter a GCP Service account credentials file!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input.TextArea rows={10} />
|
<Input.TextArea rows={10} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -66,6 +68,5 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default GcpPubSubIntegrationForm;
|
export default GcpPubSubIntegrationForm;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { Card, Button, Form, Input } from "antd";
|
import { Card, Button, Form, Input } from "antd";
|
||||||
@ -15,39 +15,27 @@ interface IProps {
|
|||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function GenerateMqttCertificate(props: IProps) {
|
||||||
certificate?: GenerateMqttIntegrationClientCertificateResponse;
|
const [certificate, setCertificate] = useState<GenerateMqttIntegrationClientCertificateResponse | undefined>(
|
||||||
buttonDisabled: boolean;
|
undefined,
|
||||||
}
|
);
|
||||||
|
const [buttonDisabled, setButtonDisabled] = useState<boolean>(false);
|
||||||
|
|
||||||
class GenerateMqttCertificate extends Component<IProps, IState> {
|
const requestCertificate = () => {
|
||||||
constructor(props: IProps) {
|
setButtonDisabled(true);
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
certificate: undefined,
|
|
||||||
buttonDisabled: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
requestCertificate = () => {
|
|
||||||
this.setState({
|
|
||||||
buttonDisabled: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let req = new GenerateMqttIntegrationClientCertificateRequest();
|
let req = new GenerateMqttIntegrationClientCertificateRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
ApplicationStore.generateMqttIntegrationClientCertificate(
|
ApplicationStore.generateMqttIntegrationClientCertificate(
|
||||||
req,
|
req,
|
||||||
(resp: GenerateMqttIntegrationClientCertificateResponse) => {
|
(resp: GenerateMqttIntegrationClientCertificateResponse) => {
|
||||||
this.setState({
|
setCertificate(resp);
|
||||||
certificate: resp,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderRequest = () => {
|
const renderRequest = () => {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<p>
|
<p>
|
||||||
@ -59,7 +47,7 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
|
|||||||
</strong>
|
</strong>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<Button onClick={this.requestCertificate} disabled={this.state.buttonDisabled}>
|
<Button onClick={requestCertificate} disabled={buttonDisabled}>
|
||||||
Generate certificate
|
Generate certificate
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
@ -67,14 +55,14 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderResponse = () => {
|
const renderResponse = () => {
|
||||||
const certificate = this.state.certificate!;
|
const cert = certificate!;
|
||||||
|
|
||||||
const initial = {
|
const initial = {
|
||||||
expiresAt: moment(certificate.getExpiresAt()!.toDate()!).format("YYYY-MM-DD HH:mm:ss"),
|
expiresAt: moment(cert.getExpiresAt()!.toDate()!).format("YYYY-MM-DD HH:mm:ss"),
|
||||||
caCert: certificate.getCaCert(),
|
caCert: cert.getCaCert(),
|
||||||
tlsCert: certificate.getTlsCert(),
|
tlsCert: cert.getTlsCert(),
|
||||||
tlsKey: certificate.getTlsKey(),
|
tlsKey: cert.getTlsKey(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -103,15 +91,13 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
let content = renderRequest();
|
||||||
let content = this.renderRequest();
|
|
||||||
|
|
||||||
if (this.state.certificate !== undefined) {
|
if (certificate !== undefined) {
|
||||||
content = this.renderResponse();
|
content = renderResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Card title="Generate MQTT certificate">{content}</Card>;
|
return <Card title="Generate MQTT certificate">{content}</Card>;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default GenerateMqttCertificate;
|
export default GenerateMqttCertificate;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -13,28 +12,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HttpCard extends Component<IProps> {
|
function HttpCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteHttpIntegrationRequest();
|
let req = new DeleteHttpIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteHttpIntegration(req, () => {});
|
ApplicationStore.deleteHttpIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/http/create">
|
<Link to="http/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/http/edit">
|
<Link to="http/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -53,6 +51,5 @@ class HttpCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default HttpCard;
|
export default HttpCard;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Button, Select, Row, Col, Typography, Space } from "antd";
|
import { Form, Input, Button, Select, Row, Col, Typography, Space } from "antd";
|
||||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
@ -10,9 +8,9 @@ interface IProps {
|
|||||||
onFinish: (obj: HttpIntegration) => void;
|
onFinish: (obj: HttpIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HttpIntegrationForm extends Component<IProps> {
|
function HttpIntegrationForm(props: IProps) {
|
||||||
onFinish = (values: HttpIntegration.AsObject) => {
|
const onFinish = (values: HttpIntegration.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let i = new HttpIntegration();
|
let i = new HttpIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
@ -24,12 +22,11 @@ class HttpIntegrationForm extends Component<IProps> {
|
|||||||
i.getHeadersMap().set(elm[0], elm[1]);
|
i.getHeadersMap().set(elm[0], elm[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Payload encoding"
|
label="Payload encoding"
|
||||||
name="encoding"
|
name="encoding"
|
||||||
@ -97,6 +94,5 @@ class HttpIntegrationForm extends Component<IProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default HttpIntegrationForm;
|
export default HttpIntegrationForm;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -13,28 +12,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class IftttCard extends Component<IProps> {
|
function IftttCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteIftttIntegrationRequest();
|
let req = new DeleteIftttIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteIftttIntegration(req, () => {});
|
ApplicationStore.deleteIftttIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/ifttt/create">
|
<Link to="ifttt/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/ifttt/edit">
|
<Link to="ifttt/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -53,6 +51,5 @@ class IftttCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default IftttCard;
|
export default IftttCard;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { Form, Input, AutoComplete, Button, Row, Col, Switch } from "antd";
|
import { Form, Input, AutoComplete, Button, Row, Col, Switch } from "antd";
|
||||||
|
|
||||||
@ -10,29 +10,15 @@ interface IProps {
|
|||||||
onFinish: (obj: IftttIntegration) => void;
|
onFinish: (obj: IftttIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function IftttIntegrationForm(props: IProps) {
|
||||||
arbitraryJson: boolean;
|
const [arbitraryJson, setArbitraryJson] = useState<Boolean>(false);
|
||||||
}
|
|
||||||
|
|
||||||
class IftttIntegrationForm extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
setArbitraryJson(props.initialValues.getArbitraryJson());
|
||||||
super(props);
|
}, [props]);
|
||||||
|
|
||||||
this.state = {
|
const onFinish = (values: IftttIntegration.AsObject) => {
|
||||||
arbitraryJson: false,
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const v = this.props.initialValues;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
arbitraryJson: v.getArbitraryJson(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (values: IftttIntegration.AsObject) => {
|
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
|
||||||
let i = new IftttIntegration();
|
let i = new IftttIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
@ -41,24 +27,21 @@ class IftttIntegrationForm extends Component<IProps, IState> {
|
|||||||
i.setArbitraryJson(v.arbitraryJson);
|
i.setArbitraryJson(v.arbitraryJson);
|
||||||
i.setUplinkValuesList(v.uplinkValuesList);
|
i.setUplinkValuesList(v.uplinkValuesList);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
onArbitraryJsonChange = (checked: boolean) => {
|
const onArbitraryJsonChange = (checked: boolean) => {
|
||||||
this.setState({
|
setArbitraryJson(checked);
|
||||||
arbitraryJson: checked,
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const options: {
|
const options: {
|
||||||
value: string;
|
value: string;
|
||||||
}[] = this.props.measurementKeys.map(v => {
|
}[] = props.measurementKeys.map(v => {
|
||||||
return { value: v };
|
return { value: v };
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Key"
|
label="Key"
|
||||||
name="key"
|
name="key"
|
||||||
@ -72,7 +55,12 @@ class IftttIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
label="Event prefix"
|
label="Event prefix"
|
||||||
name="eventPrefix"
|
name="eventPrefix"
|
||||||
rules={[{ pattern: /[A-Za-z0-9]+/, message: "Only use A-Z, a-z and 0-9 characters" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
pattern: /[A-Za-z0-9]+/,
|
||||||
|
message: "Only use A-Z, a-z and 0-9 characters",
|
||||||
|
},
|
||||||
|
]}
|
||||||
tooltip="The prefix will be added to the Webhook event, e.g. if set an uplink will be published as PREFIX_up instead of up."
|
tooltip="The prefix will be added to the Webhook event, e.g. if set an uplink will be published as PREFIX_up instead of up."
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
@ -85,11 +73,12 @@ class IftttIntegrationForm extends Component<IProps, IState> {
|
|||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
tooltip="If enabled, the event payload will be published as-is (arbitrary JSON payload instead of 3 JSON values format)."
|
tooltip="If enabled, the event payload will be published as-is (arbitrary JSON payload instead of 3 JSON values format)."
|
||||||
>
|
>
|
||||||
<Switch onChange={this.onArbitraryJsonChange} />
|
<Switch onChange={onArbitraryJsonChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{!this.state.arbitraryJson && <Form.List name="uplinkValuesList">
|
{!arbitraryJson && (
|
||||||
|
<Form.List name="uplinkValuesList">
|
||||||
{fields => (
|
{fields => (
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
{fields.map((field, i) => (
|
{fields.map((field, i) => (
|
||||||
@ -105,7 +94,8 @@ class IftttIntegrationForm extends Component<IProps, IState> {
|
|||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
</Form.List>}
|
</Form.List>
|
||||||
|
)}
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" htmlType="submit">
|
<Button type="primary" htmlType="submit">
|
||||||
Submit
|
Submit
|
||||||
@ -114,6 +104,5 @@ class IftttIntegrationForm extends Component<IProps, IState> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default IftttIntegrationForm;
|
export default IftttIntegrationForm;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { Form, Input, Button, Select } from "antd";
|
import { Form, Input, Button, Select } from "antd";
|
||||||
|
|
||||||
@ -13,20 +13,11 @@ interface IProps {
|
|||||||
onFinish: (obj: InfluxDbIntegration) => void;
|
onFinish: (obj: InfluxDbIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function InfluxDbIntegrationForm(props: IProps) {
|
||||||
selectedVersion: InfluxDbVersion;
|
const [selectedVersion, setSelectedVersion] = useState<InfluxDbVersion>(InfluxDbVersion.INFLUXDB_1);
|
||||||
}
|
|
||||||
|
|
||||||
class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
const onFinish = (values: InfluxDbIntegration.AsObject) => {
|
||||||
constructor(props: IProps) {
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
selectedVersion: InfluxDbVersion.INFLUXDB_1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (values: InfluxDbIntegration.AsObject) => {
|
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
|
||||||
let i = new InfluxDbIntegration();
|
let i = new InfluxDbIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
@ -41,24 +32,21 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
|||||||
i.setBucket(v.bucket);
|
i.setBucket(v.bucket);
|
||||||
i.setToken(v.token);
|
i.setToken(v.token);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
onVersionChange = (version: InfluxDbVersion) => {
|
const onVersionChange = (version: InfluxDbVersion) => {
|
||||||
this.setState({
|
setSelectedVersion(version);
|
||||||
selectedVersion: version,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="InfluxDB version"
|
label="InfluxDB version"
|
||||||
name="version"
|
name="version"
|
||||||
rules={[{ required: true, message: "Please select an InfluxDB version!" }]}
|
rules={[{ required: true, message: "Please select an InfluxDB version!" }]}
|
||||||
>
|
>
|
||||||
<Select onChange={this.onVersionChange}>
|
<Select onChange={onVersionChange}>
|
||||||
<Select.Option value={InfluxDbVersion.INFLUXDB_1}>InfluxDB v1</Select.Option>
|
<Select.Option value={InfluxDbVersion.INFLUXDB_1}>InfluxDB v1</Select.Option>
|
||||||
<Select.Option value={InfluxDbVersion.INFLUXDB_2}>InfluxDB v2</Select.Option>
|
<Select.Option value={InfluxDbVersion.INFLUXDB_2}>InfluxDB v2</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
@ -70,26 +58,22 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
|||||||
>
|
>
|
||||||
<Input placeholder="http://localhost:8086/api/v2/write" />
|
<Input placeholder="http://localhost:8086/api/v2/write" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||||
<Form.Item label="Username" name="username">
|
<Form.Item label="Username" name="username">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||||
<Form.Item label="Password" name="password">
|
<Form.Item label="Password" name="password">
|
||||||
<Input.Password />
|
<Input.Password />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||||
<Form.Item
|
<Form.Item label="Database name" name="db" rules={[{ required: true, message: "Please enter database name!" }]}>
|
||||||
label="Database name"
|
|
||||||
name="db"
|
|
||||||
rules={[{ required: true, message: "Please enter database name!" }]}
|
|
||||||
>
|
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Retention policy name"
|
label="Retention policy name"
|
||||||
name="retentionPolicyName"
|
name="retentionPolicyName"
|
||||||
@ -98,7 +82,7 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||||
<Form.Item label="Select timestamp precision" name="precision">
|
<Form.Item label="Select timestamp precision" name="precision">
|
||||||
<Select>
|
<Select>
|
||||||
<Select.Option value={InfluxDbPrecision.NS}>Nanosecond</Select.Option>
|
<Select.Option value={InfluxDbPrecision.NS}>Nanosecond</Select.Option>
|
||||||
@ -110,17 +94,17 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
|||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
||||||
<Form.Item label="Organization" name="organization">
|
<Form.Item label="Organization" name="organization">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
||||||
<Form.Item label="Bucket" name="bucket">
|
<Form.Item label="Bucket" name="bucket">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
{selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
||||||
<Form.Item label="Token" name="token">
|
<Form.Item label="Token" name="token">
|
||||||
<Input.Password />
|
<Input.Password />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -133,6 +117,5 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default InfluxDbIntegrationForm;
|
export default InfluxDbIntegrationForm;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -13,28 +12,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class InfluxdbCard extends Component<IProps> {
|
function InfluxdbCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteInfluxDbIntegrationRequest();
|
let req = new DeleteInfluxDbIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteInfluxDbIntegration(req, () => {});
|
ApplicationStore.deleteInfluxDbIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/influxdb/create">
|
<Link to="influxdb/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/influxdb/edit">
|
<Link to="influxdb/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -53,6 +51,5 @@ class InfluxdbCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default InfluxdbCard;
|
export default InfluxdbCard;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -13,28 +12,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoRaCloudCard extends Component<IProps> {
|
function LoRaCloudCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteLoraCloudIntegrationRequest();
|
let req = new DeleteLoraCloudIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteLoraCloudIntegration(req, () => {});
|
ApplicationStore.deleteLoraCloudIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/loracloud/create">
|
<Link to="loracloud/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/loracloud/edit">
|
<Link to="loracloud/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -53,6 +51,5 @@ class LoRaCloudCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default LoRaCloudCard;
|
export default LoRaCloudCard;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { Form, Input, InputNumber, Switch, Button, Tabs, Collapse } from "antd";
|
import { Form, Input, InputNumber, Switch, Button, Tabs, Collapse } from "antd";
|
||||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
@ -13,43 +13,28 @@ interface IProps {
|
|||||||
onFinish: (obj: LoraCloudIntegration) => void;
|
onFinish: (obj: LoraCloudIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function LoRaCloudIntegrationForm(props: IProps) {
|
||||||
modemEnabled: boolean;
|
const [modemEnabled, setModemEnabled] = useState<boolean>(false);
|
||||||
geolocationTdoa: boolean;
|
const [geolocationTdoa, setGeolocationTdoa] = useState<boolean>(false);
|
||||||
geolocationRssi: boolean;
|
const [geolocationRssi, setGeolocationRssi] = useState<boolean>(false);
|
||||||
geolocationWifi: boolean;
|
const [geolocationWifi, setGeolocationWifi] = useState<boolean>(false);
|
||||||
geolocationGnss: boolean;
|
const [geolocationGnss, setGeolocationGnss] = useState<boolean>(false);
|
||||||
}
|
|
||||||
|
|
||||||
class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
const v = props.initialValues;
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
modemEnabled: false,
|
|
||||||
geolocationTdoa: false,
|
|
||||||
geolocationRssi: false,
|
|
||||||
geolocationWifi: false,
|
|
||||||
geolocationGnss: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const v = this.props.initialValues;
|
|
||||||
const mgs = v.getModemGeolocationServices();
|
const mgs = v.getModemGeolocationServices();
|
||||||
|
|
||||||
if (mgs !== undefined) {
|
if (mgs !== undefined) {
|
||||||
this.setState({
|
setModemEnabled(mgs.getModemEnabled());
|
||||||
modemEnabled: mgs.getModemEnabled(),
|
setGeolocationTdoa(mgs.getGeolocationTdoa());
|
||||||
geolocationTdoa: mgs.getGeolocationTdoa(),
|
setGeolocationRssi(mgs.getGeolocationRssi());
|
||||||
geolocationRssi: mgs.getGeolocationRssi(),
|
setGeolocationWifi(mgs.getGeolocationWifi());
|
||||||
geolocationWifi: mgs.getGeolocationWifi(),
|
setGeolocationGnss(mgs.getGeolocationGnss());
|
||||||
geolocationGnss: mgs.getGeolocationGnss(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
onFinish = (values: LoraCloudIntegration.AsObject) => {
|
const onFinish = (values: LoraCloudIntegration.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
const mgsv = v.modemGeolocationServices;
|
const mgsv = v.modemGeolocationServices;
|
||||||
|
|
||||||
let mgs = new LoraCloudModemGeolocationServices();
|
let mgs = new LoraCloudModemGeolocationServices();
|
||||||
@ -76,42 +61,31 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
i.setModemGeolocationServices(mgs);
|
i.setModemGeolocationServices(mgs);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
onModemEnabledChange = (v: boolean) => {
|
const onModemEnabledChange = (v: boolean) => {
|
||||||
this.setState({
|
setModemEnabled(v);
|
||||||
modemEnabled: v,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onGeolocationTdoaChange = (v: boolean) => {
|
const onGeolocationTdoaChange = (v: boolean) => {
|
||||||
this.setState({
|
setGeolocationTdoa(v);
|
||||||
geolocationTdoa: v,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onGeolocationRssiChange = (v: boolean) => {
|
const onGeolocationRssiChange = (v: boolean) => {
|
||||||
this.setState({
|
setGeolocationRssi(v);
|
||||||
geolocationRssi: v,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onGeolocationWifiChange = (v: boolean) => {
|
const onGeolocationWifiChange = (v: boolean) => {
|
||||||
this.setState({
|
setGeolocationWifi(v);
|
||||||
geolocationWifi: v,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onGeolocationGnssChange = (v: boolean) => {
|
const onGeolocationGnssChange = (v: boolean) => {
|
||||||
this.setState({
|
setGeolocationGnss(v);
|
||||||
geolocationGnss: v,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tabs.TabPane tab="Modem & Geolocation Services" key="1">
|
<Tabs.TabPane tab="Modem & Geolocation Services" key="1">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -127,9 +101,9 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
label="I am using LoRa Edge™ LR1110 or my device uses LoRa Basics™ Modem-E"
|
label="I am using LoRa Edge™ LR1110 or my device uses LoRa Basics™ Modem-E"
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch onChange={this.onModemEnabledChange} />
|
<Switch onChange={onModemEnabledChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{this.state.modemEnabled && (
|
{modemEnabled && (
|
||||||
<Form.List name={["modemGeolocationServices", "forwardFPortsList"]}>
|
<Form.List name={["modemGeolocationServices", "forwardFPortsList"]}>
|
||||||
{(fields, { add, remove }) => (
|
{(fields, { add, remove }) => (
|
||||||
<Form.Item label="Forward messages on these FPorts to LoRa Cloud">
|
<Form.Item label="Forward messages on these FPorts to LoRa Cloud">
|
||||||
@ -137,7 +111,11 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
{...field}
|
{...field}
|
||||||
rules={[{ required: true, message: "Please a FPort value!" }]}
|
rules={[{ required: true, message: "Please a FPort value!" }]}
|
||||||
style={{ display: "inline-block", width: "100px", marginRight: "24px" }}
|
style={{
|
||||||
|
display: "inline-block",
|
||||||
|
width: "100px",
|
||||||
|
marginRight: "24px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
min={1}
|
min={1}
|
||||||
@ -151,7 +129,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
)}
|
)}
|
||||||
</Form.List>
|
</Form.List>
|
||||||
)}
|
)}
|
||||||
{this.state.modemEnabled && (
|
{modemEnabled && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Use receive timestamp for GNSS geolocation"
|
label="Use receive timestamp for GNSS geolocation"
|
||||||
name={["modemGeolocationServices", "gnssUseRxTime"]}
|
name={["modemGeolocationServices", "gnssUseRxTime"]}
|
||||||
@ -161,7 +139,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.modemEnabled && (
|
{modemEnabled && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Use location of receiving gateways for assistance"
|
label="Use location of receiving gateways for assistance"
|
||||||
name={["modemGeolocationServices", "gnssUseGatewayLocation"]}
|
name={["modemGeolocationServices", "gnssUseGatewayLocation"]}
|
||||||
@ -171,7 +149,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.modemEnabled && (
|
{modemEnabled && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="My device adheres to the LoRa Edge™ Tracker Modem-E Version Reference Design protocol"
|
label="My device adheres to the LoRa Edge™ Tracker Modem-E Version Reference Design protocol"
|
||||||
name={["modemGeolocationServices", "parseTlv"]}
|
name={["modemGeolocationServices", "parseTlv"]}
|
||||||
@ -189,7 +167,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
tooltip="If enabled, geolocation will be based on time-difference of arrival (TDOA). Please note that this requires gateways that support the fine-timestamp feature."
|
tooltip="If enabled, geolocation will be based on time-difference of arrival (TDOA). Please note that this requires gateways that support the fine-timestamp feature."
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch onChange={this.onGeolocationTdoaChange} />
|
<Switch onChange={onGeolocationTdoaChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="RSSI based geolocation"
|
label="RSSI based geolocation"
|
||||||
@ -197,7 +175,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
tooltip="If enabled, geolocation will be based on RSSI values reported by the receiving gateways."
|
tooltip="If enabled, geolocation will be based on RSSI values reported by the receiving gateways."
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch onChange={this.onGeolocationRssiChange} />
|
<Switch onChange={onGeolocationRssiChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Wi-Fi based geolocation"
|
label="Wi-Fi based geolocation"
|
||||||
@ -205,7 +183,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
tooltip="If enabled, geolocation will be based on Wi-Fi access-point data reported by the device."
|
tooltip="If enabled, geolocation will be based on Wi-Fi access-point data reported by the device."
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch onChange={this.onGeolocationWifiChange} />
|
<Switch onChange={onGeolocationWifiChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="GNSS based geolocation (LR1110)"
|
label="GNSS based geolocation (LR1110)"
|
||||||
@ -213,9 +191,9 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
tooltip="If enabled, geolocation will be based on GNSS data reported by the device."
|
tooltip="If enabled, geolocation will be based on GNSS data reported by the device."
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch onChange={this.onGeolocationGnssChange} />
|
<Switch onChange={onGeolocationGnssChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{(this.state.geolocationTdoa || this.state.geolocationRssi) && (
|
{(geolocationTdoa || geolocationRssi) && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Geolocation buffer (TTL in seconds)"
|
label="Geolocation buffer (TTL in seconds)"
|
||||||
name={["modemGeolocationServices", "geolocationBufferTtl"]}
|
name={["modemGeolocationServices", "geolocationBufferTtl"]}
|
||||||
@ -224,7 +202,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
<InputNumber min={0} max={86400} />
|
<InputNumber min={0} max={86400} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{(this.state.geolocationTdoa || this.state.geolocationRssi) && (
|
{(geolocationTdoa || geolocationRssi) && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Geolocation min buffer size"
|
label="Geolocation min buffer size"
|
||||||
name={["modemGeolocationServices", "geolocationMinBufferSize"]}
|
name={["modemGeolocationServices", "geolocationMinBufferSize"]}
|
||||||
@ -233,7 +211,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.geolocationWifi && (
|
{geolocationWifi && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Wifi payload field"
|
label="Wifi payload field"
|
||||||
name={["modemGeolocationServices", "geolocationWifiPayloadField"]}
|
name={["modemGeolocationServices", "geolocationWifiPayloadField"]}
|
||||||
@ -242,7 +220,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.geolocationGnss && (
|
{geolocationGnss && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="GNSS payload field"
|
label="GNSS payload field"
|
||||||
name={["modemGeolocationServices", "geolocationGnssPayloadField"]}
|
name={["modemGeolocationServices", "geolocationGnssPayloadField"]}
|
||||||
@ -251,7 +229,7 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{this.state.geolocationGnss && (
|
{geolocationGnss && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Use receive timestamp for GNSS geolocation"
|
label="Use receive timestamp for GNSS geolocation"
|
||||||
name={["modemGeolocationServices", "geolocationGnssUseRxTime"]}
|
name={["modemGeolocationServices", "geolocationGnssUseRxTime"]}
|
||||||
@ -272,6 +250,5 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default LoRaCloudIntegrationForm;
|
export default LoRaCloudIntegrationForm;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card } from "antd";
|
import { Col, Card } from "antd";
|
||||||
@ -9,9 +8,8 @@ interface IProps {
|
|||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HttpCard extends Component<IProps> {
|
function MqttCard(props: IProps) {
|
||||||
render() {
|
let actions: any[] = [<Link to="mqtt/certificate">Get certificate</Link>];
|
||||||
let actions: any[] = [<Link to="integrations/mqtt/certificate">Get certificate</Link>];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
@ -26,6 +24,5 @@ class HttpCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default HttpCard;
|
export default MqttCard;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -13,28 +12,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyDevicesCard extends Component<IProps> {
|
function MyDevicesCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteMyDevicesIntegrationRequest();
|
let req = new DeleteMyDevicesIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteMyDevicesIntegration(req, () => {});
|
ApplicationStore.deleteMyDevicesIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/mydevices/create">
|
<Link to="mydevices/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/mydevices/edit">
|
<Link to="mydevices/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -53,6 +51,5 @@ class MyDevicesCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default MyDevicesCard;
|
export default MyDevicesCard;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { Form, Input, Button, Select } from "antd";
|
import { Form, Input, Button, Select } from "antd";
|
||||||
|
|
||||||
@ -9,55 +9,40 @@ interface IProps {
|
|||||||
onFinish: (obj: MyDevicesIntegration) => void;
|
onFinish: (obj: MyDevicesIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function MyDevicesIntegrationForm(props: IProps) {
|
||||||
selectedEndpoint: string;
|
const [selectedEndpoint, setSelectedEndpoint] = useState<string>("");
|
||||||
customEndpoint: string;
|
const [customEndpoint, setCustomEndpoint] = useState<string>("");
|
||||||
}
|
|
||||||
|
|
||||||
class MyDevicesIntegrationForm extends Component<IProps, IState> {
|
const onFinish = (values: MyDevicesIntegration.AsObject) => {
|
||||||
constructor(props: IProps) {
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
selectedEndpoint: "",
|
|
||||||
customEndpoint: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (values: MyDevicesIntegration.AsObject) => {
|
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
|
||||||
let i = new MyDevicesIntegration();
|
let i = new MyDevicesIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
if (v.endpoint === "custom") {
|
if (v.endpoint === "custom") {
|
||||||
i.setEndpoint(this.state.customEndpoint);
|
i.setEndpoint(customEndpoint);
|
||||||
} else {
|
} else {
|
||||||
i.setEndpoint(v.endpoint);
|
i.setEndpoint(v.endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
onEndpointChange = (v: string) => {
|
const onEndpointChange = (v: string) => {
|
||||||
this.setState({
|
setSelectedEndpoint(v);
|
||||||
selectedEndpoint: v,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onCustomEndpointChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const onCustomEndpointChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
this.setState({
|
setCustomEndpoint(e.target.value);
|
||||||
customEndpoint: e.target.value,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Select myDevices endpoint"
|
label="Select myDevices endpoint"
|
||||||
name="endpoint"
|
name="endpoint"
|
||||||
rules={[{ required: true, message: "Please select a myDevices endpoint!" }]}
|
rules={[{ required: true, message: "Please select a myDevices endpoint!" }]}
|
||||||
>
|
>
|
||||||
<Select onChange={this.onEndpointChange}>
|
<Select onChange={onEndpointChange}>
|
||||||
<Select.Option value="https://lora.mydevices.com/v1/networks/chirpstackio/uplink">Cayenne</Select.Option>
|
<Select.Option value="https://lora.mydevices.com/v1/networks/chirpstackio/uplink">Cayenne</Select.Option>
|
||||||
<Select.Option value="https://lora.iotinabox.com/v1/networks/iotinabox.chirpstackio/uplink">
|
<Select.Option value="https://lora.iotinabox.com/v1/networks/iotinabox.chirpstackio/uplink">
|
||||||
IoT in a Box
|
IoT in a Box
|
||||||
@ -65,13 +50,13 @@ class MyDevicesIntegrationForm extends Component<IProps, IState> {
|
|||||||
<Select.Option value="custom">Custom endpoint URL</Select.Option>
|
<Select.Option value="custom">Custom endpoint URL</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{this.state.selectedEndpoint === "custom" && (
|
{selectedEndpoint === "custom" && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="myDevices API endpoint"
|
label="myDevices API endpoint"
|
||||||
name="customEndpoint"
|
name="customEndpoint"
|
||||||
rules={[{ required: true, message: "Please enter an API endpoint!" }]}
|
rules={[{ required: true, message: "Please enter an API endpoint!" }]}
|
||||||
>
|
>
|
||||||
<Input onChange={this.onCustomEndpointChange} />
|
<Input onChange={onCustomEndpointChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
@ -82,6 +67,5 @@ class MyDevicesIntegrationForm extends Component<IProps, IState> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default MyDevicesIntegrationForm;
|
export default MyDevicesIntegrationForm;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -16,28 +15,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PilotThingsCard extends Component<IProps> {
|
function PilotThingsCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeletePilotThingsIntegrationRequest();
|
let req = new DeletePilotThingsIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deletePilotThingsIntegration(req, () => {});
|
ApplicationStore.deletePilotThingsIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/pilot-things/create">
|
<Link to="pilot-things/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/pilot-things/edit">
|
<Link to="pilot-things/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -56,6 +54,5 @@ class PilotThingsCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default PilotThingsCard;
|
export default PilotThingsCard;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Button } from "antd";
|
import { Form, Input, Button } from "antd";
|
||||||
|
|
||||||
import { PilotThingsIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { PilotThingsIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -9,21 +7,20 @@ interface IProps {
|
|||||||
onFinish: (obj: PilotThingsIntegration) => void;
|
onFinish: (obj: PilotThingsIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PilotThingsIntegrationForm extends Component<IProps> {
|
function PilotThingsIntegrationForm(props: IProps) {
|
||||||
onFinish = (values: PilotThingsIntegration.AsObject) => {
|
const onFinish = (values: PilotThingsIntegration.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let i = new PilotThingsIntegration();
|
let i = new PilotThingsIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
i.setServer(v.server);
|
i.setServer(v.server);
|
||||||
i.setToken(v.token);
|
i.setToken(v.token);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Pilot Things server"
|
label="Pilot Things server"
|
||||||
name="server"
|
name="server"
|
||||||
@ -46,6 +43,5 @@ class PilotThingsIntegrationForm extends Component<IProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default PilotThingsIntegrationForm;
|
export default PilotThingsIntegrationForm;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Col, Card, Popconfirm } from "antd";
|
import { Col, Card, Popconfirm } from "antd";
|
||||||
@ -16,28 +15,27 @@ interface IProps {
|
|||||||
add?: boolean;
|
add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThingsBoardCard extends Component<IProps> {
|
function ThingsBoardCard(props: IProps) {
|
||||||
onDelete = () => {
|
const onDelete = () => {
|
||||||
let req = new DeleteThingsBoardIntegrationRequest();
|
let req = new DeleteThingsBoardIntegrationRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
ApplicationStore.deleteThingsBoardIntegration(req, () => {});
|
ApplicationStore.deleteThingsBoardIntegration(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let actions: any[] = [];
|
let actions: any[] = [];
|
||||||
|
|
||||||
if (!!this.props.add) {
|
if (!!props.add) {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/thingsboard/create">
|
<Link to="thingsboard/create">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
actions = [
|
actions = [
|
||||||
<Link to="integrations/thingsboard/edit">
|
<Link to="thingsboard/edit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Link>,
|
</Link>,
|
||||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={onDelete}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
];
|
];
|
||||||
@ -56,6 +54,5 @@ class ThingsBoardCard extends Component<IProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ThingsBoardCard;
|
export default ThingsBoardCard;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Button, Typography } from "antd";
|
import { Form, Input, Button, Typography } from "antd";
|
||||||
|
|
||||||
import { ThingsBoardIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { ThingsBoardIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -9,24 +7,28 @@ interface IProps {
|
|||||||
onFinish: (obj: ThingsBoardIntegration) => void;
|
onFinish: (obj: ThingsBoardIntegration) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThingsBoardIntegrationForm extends Component<IProps> {
|
function ThingsBoardIntegrationForm(props: IProps) {
|
||||||
onFinish = (values: ThingsBoardIntegration.AsObject) => {
|
const onFinish = (values: ThingsBoardIntegration.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let i = new ThingsBoardIntegration();
|
let i = new ThingsBoardIntegration();
|
||||||
|
|
||||||
i.setApplicationId(v.applicationId);
|
i.setApplicationId(v.applicationId);
|
||||||
i.setServer(v.server);
|
i.setServer(v.server);
|
||||||
|
|
||||||
this.props.onFinish(i);
|
props.onFinish(i);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="ThingsBoard server"
|
label="ThingsBoard server"
|
||||||
name="server"
|
name="server"
|
||||||
rules={[{ required: true, message: "Please enter the address to the ThingsBoard server!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter the address to the ThingsBoard server!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input placeholder="http://host:port" />
|
<Input placeholder="http://host:port" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -44,6 +46,5 @@ class ThingsBoardIntegrationForm extends Component<IProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ThingsBoardIntegrationForm;
|
export default ThingsBoardIntegrationForm;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { presetPalettes } from "@ant-design/colors";
|
import { presetPalettes } from "@ant-design/colors";
|
||||||
import { Space, Breadcrumb, Card, Row, Col, PageHeader, Empty } from "antd";
|
import { Space, Breadcrumb, Card, Row, Col, Empty } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { LatLngTuple, PointTuple } from "leaflet";
|
import { LatLngTuple, PointTuple } from "leaflet";
|
||||||
@ -27,35 +28,18 @@ import InternalStore from "../../stores/InternalStore";
|
|||||||
import GatewayStore from "../../stores/GatewayStore";
|
import GatewayStore from "../../stores/GatewayStore";
|
||||||
import Map, { Marker, MarkerColor } from "../../components/Map";
|
import Map, { Marker, MarkerColor } from "../../components/Map";
|
||||||
|
|
||||||
interface GatewaysMapState {
|
function GatewaysMap() {
|
||||||
items: GatewayListItem[];
|
const [items, setItems] = useState<GatewayListItem[]>([]);
|
||||||
}
|
|
||||||
|
|
||||||
class GatewaysMap extends Component<{}, GatewaysMapState> {
|
useEffect(() => {
|
||||||
constructor(props: {}) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
items: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData = () => {
|
|
||||||
let req = new ListGatewaysRequest();
|
let req = new ListGatewaysRequest();
|
||||||
req.setLimit(9999);
|
req.setLimit(9999);
|
||||||
GatewayStore.list(req, (resp: ListGatewaysResponse) => {
|
GatewayStore.list(req, (resp: ListGatewaysResponse) => {
|
||||||
this.setState({
|
setItems(resp.getResultList());
|
||||||
items: resp.getResultList(),
|
|
||||||
});
|
});
|
||||||
});
|
}, []);
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
if (items.length === 0) {
|
||||||
if (this.state.items.length === 0) {
|
|
||||||
return <Empty />;
|
return <Empty />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +52,7 @@ class GatewaysMap extends Component<{}, GatewaysMapState> {
|
|||||||
let bounds: LatLngTuple[] = [];
|
let bounds: LatLngTuple[] = [];
|
||||||
let markers: any[] = [];
|
let markers: any[] = [];
|
||||||
|
|
||||||
for (const item of this.state.items) {
|
for (const item of items) {
|
||||||
const pos: LatLngTuple = [item.getLocation()!.getLatitude(), item.getLocation()!.getLongitude()];
|
const pos: LatLngTuple = [item.getLocation()!.getLatitude(), item.getLocation()!.getLongitude()];
|
||||||
bounds.push(pos);
|
bounds.push(pos);
|
||||||
|
|
||||||
@ -106,58 +90,11 @@ class GatewaysMap extends Component<{}, GatewaysMapState> {
|
|||||||
</Map>
|
</Map>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface GatewayProps {
|
function DevicesActiveInactive({ summary }: { summary?: GetDevicesSummaryResponse }) {
|
||||||
summary?: GetGatewaysSummaryResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GatewaysActiveInactive extends Component<GatewayProps> {
|
|
||||||
render() {
|
|
||||||
if (
|
if (
|
||||||
this.props.summary === undefined ||
|
summary === undefined ||
|
||||||
(this.props.summary.getNeverSeenCount() === 0 &&
|
(summary.getNeverSeenCount() === 0 && summary.getInactiveCount() === 0 && summary.getActiveCount() === 0)
|
||||||
this.props.summary.getOfflineCount() === 0 &&
|
|
||||||
this.props.summary.getOnlineCount() === 0)
|
|
||||||
) {
|
|
||||||
return <Empty />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
labels: ["Never seen", "Offline", "Online"],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: [
|
|
||||||
this.props.summary.getNeverSeenCount(),
|
|
||||||
this.props.summary.getOfflineCount(),
|
|
||||||
this.props.summary.getOnlineCount(),
|
|
||||||
],
|
|
||||||
backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const options: {
|
|
||||||
animation: false;
|
|
||||||
} = {
|
|
||||||
animation: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DeviceProps {
|
|
||||||
summary?: GetDevicesSummaryResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DevicesActiveInactive extends Component<DeviceProps> {
|
|
||||||
render() {
|
|
||||||
if (
|
|
||||||
this.props.summary === undefined ||
|
|
||||||
(this.props.summary.getNeverSeenCount() === 0 &&
|
|
||||||
this.props.summary.getInactiveCount() === 0 &&
|
|
||||||
this.props.summary.getActiveCount() === 0)
|
|
||||||
) {
|
) {
|
||||||
return <Empty />;
|
return <Empty />;
|
||||||
}
|
}
|
||||||
@ -166,11 +103,7 @@ class DevicesActiveInactive extends Component<DeviceProps> {
|
|||||||
labels: ["Never seen", "Inactive", "Active"],
|
labels: ["Never seen", "Inactive", "Active"],
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
data: [
|
data: [summary.getNeverSeenCount(), summary.getInactiveCount(), summary.getActiveCount()],
|
||||||
this.props.summary.getNeverSeenCount(),
|
|
||||||
this.props.summary.getInactiveCount(),
|
|
||||||
this.props.summary.getActiveCount(),
|
|
||||||
],
|
|
||||||
backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
|
backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -184,10 +117,36 @@ class DevicesActiveInactive extends Component<DeviceProps> {
|
|||||||
|
|
||||||
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GatewaysActiveInactive({ summary }: { summary?: GetGatewaysSummaryResponse }) {
|
||||||
|
if (
|
||||||
|
summary === undefined ||
|
||||||
|
(summary.getNeverSeenCount() === 0 && summary.getOfflineCount() === 0 && summary.getOnlineCount() === 0)
|
||||||
|
) {
|
||||||
|
return <Empty />;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DevicesDataRates extends Component<DeviceProps> {
|
const data = {
|
||||||
getColor = (dr: number) => {
|
labels: ["Never seen", "Offline", "Online"],
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: [summary.getNeverSeenCount(), summary.getOfflineCount(), summary.getOnlineCount()],
|
||||||
|
backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const options: {
|
||||||
|
animation: false;
|
||||||
|
} = {
|
||||||
|
animation: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DevicesDataRates({ summary }: { summary?: GetDevicesSummaryResponse }) {
|
||||||
|
const getColor = (dr: number) => {
|
||||||
return [
|
return [
|
||||||
"#ff5722",
|
"#ff5722",
|
||||||
"#ff9800",
|
"#ff9800",
|
||||||
@ -207,8 +166,7 @@ class DevicesDataRates extends Component<DeviceProps> {
|
|||||||
][dr];
|
][dr];
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (summary === undefined || summary.getDrCountMap().toArray().length === 0) {
|
||||||
if (this.props.summary === undefined || this.props.summary.getDrCountMap().toArray().length === 0) {
|
|
||||||
return <Empty />;
|
return <Empty />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,10 +186,10 @@ class DevicesDataRates extends Component<DeviceProps> {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const elm of this.props.summary.getDrCountMap().toArray()) {
|
for (const elm of summary.getDrCountMap().toArray()) {
|
||||||
data.labels.push(`DR${elm[0]}`);
|
data.labels.push(`DR${elm[0]}`);
|
||||||
data.datasets[0].data.push(elm[1]);
|
data.datasets[0].data.push(elm[1]);
|
||||||
data.datasets[0].backgroundColor.push(this.getColor(elm[0]));
|
data.datasets[0].backgroundColor.push(getColor(elm[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
const options: {
|
const options: {
|
||||||
@ -242,36 +200,21 @@ class DevicesDataRates extends Component<DeviceProps> {
|
|||||||
|
|
||||||
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps {}
|
function Dashboard() {
|
||||||
|
const [gatewaysSummary, setGatewaysSummary] = useState<GetGatewaysSummaryResponse | undefined>(undefined);
|
||||||
|
const [devicesSummary, setDevicesSummary] = useState<GetDevicesSummaryResponse | undefined>(undefined);
|
||||||
|
|
||||||
interface IState {
|
useEffect(() => {
|
||||||
gatewaysSummary?: GetGatewaysSummaryResponse;
|
|
||||||
devicesSummary?: GetDevicesSummaryResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Dashboard extends Component<IProps, IState> {
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
InternalStore.getGatewaysSummary(new GetGatewaysSummaryRequest(), (resp: GetGatewaysSummaryResponse) => {
|
InternalStore.getGatewaysSummary(new GetGatewaysSummaryRequest(), (resp: GetGatewaysSummaryResponse) => {
|
||||||
this.setState({
|
setGatewaysSummary(resp);
|
||||||
gatewaysSummary: resp,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
InternalStore.getDevicesSummary(new GetDevicesSummaryRequest(), (resp: GetDevicesSummaryResponse) => {
|
InternalStore.getDevicesSummary(new GetDevicesSummaryRequest(), (resp: GetDevicesSummaryResponse) => {
|
||||||
this.setState({
|
setDevicesSummary(resp);
|
||||||
devicesSummary: resp,
|
|
||||||
});
|
});
|
||||||
});
|
}, []);
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
@ -290,17 +233,17 @@ class Dashboard extends Component<IProps, IState> {
|
|||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Card title="Active devices">
|
<Card title="Active devices">
|
||||||
<DevicesActiveInactive summary={this.state.devicesSummary} />
|
<DevicesActiveInactive summary={devicesSummary} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Card title="Active gateways">
|
<Card title="Active gateways">
|
||||||
<GatewaysActiveInactive summary={this.state.gatewaysSummary} />
|
<GatewaysActiveInactive summary={gatewaysSummary} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Card title="Device data-rate usage">
|
<Card title="Device data-rate usage">
|
||||||
<DevicesDataRates summary={this.state.devicesSummary} />
|
<DevicesDataRates summary={devicesSummary} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@ -310,6 +253,5 @@ class Dashboard extends Component<IProps, IState> {
|
|||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default Dashboard;
|
export default Dashboard;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { Link, RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
import { Space, Breadcrumb, Card } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
||||||
import {
|
import {
|
||||||
@ -12,17 +12,18 @@ import {
|
|||||||
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
|
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
|
||||||
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
||||||
|
|
||||||
class CreateDeviceProfileTemplate extends Component<RouteComponentProps> {
|
function CreateDeviceProfileTemplate() {
|
||||||
onFinish = (obj: DeviceProfileTemplate) => {
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const onFinish = (obj: DeviceProfileTemplate) => {
|
||||||
let req = new CreateDeviceProfileTemplateRequest();
|
let req = new CreateDeviceProfileTemplateRequest();
|
||||||
req.setDeviceProfileTemplate(obj);
|
req.setDeviceProfileTemplate(obj);
|
||||||
|
|
||||||
DeviceProfileTemplateStore.create(req, () => {
|
DeviceProfileTemplateStore.create(req, () => {
|
||||||
this.props.history.push(`/device-profile-templates`);
|
navigate(`/device-profile-templates`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const codecScript = `// Decode uplink function.
|
const codecScript = `// Decode uplink function.
|
||||||
//
|
//
|
||||||
// Input is an object with the following fields:
|
// Input is an object with the following fields:
|
||||||
@ -87,11 +88,10 @@ function encodeDownlink(input) {
|
|||||||
title="Add device-profile template"
|
title="Add device-profile template"
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<DeviceProfileTemplateForm initialValues={deviceProfileTemplate} onFinish={this.onFinish} />
|
<DeviceProfileTemplateForm initialValues={deviceProfileTemplate} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateDeviceProfileTemplate;
|
export default CreateDeviceProfileTemplate;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { Form, Input, Select, InputNumber, Switch, Row, Col, Button, Tabs, Card } from "antd";
|
import { Form, Input, Select, InputNumber, Switch, Row, Col, Button, Tabs, Card } from "antd";
|
||||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
@ -17,37 +17,20 @@ interface IProps {
|
|||||||
update?: boolean;
|
update?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DeviceProfileTemplateForm(props: IProps) {
|
||||||
supportsOtaa: boolean;
|
const [form] = Form.useForm();
|
||||||
supportsClassB: boolean;
|
const [supportsOtaa, setSupportsOtaa] = useState<boolean>(false);
|
||||||
supportsClassC: boolean;
|
const [supportsClassB, setSupportsClassB] = useState<boolean>(false);
|
||||||
payloadCodecRuntime: CodecRuntime;
|
const [supportsClassC, setSupportsClassC] = useState<boolean>(false);
|
||||||
adrAlgorithms: [string, string][];
|
const [payloadCodecRuntime, setPayloadCodecRuntime] = useState<CodecRuntime>(CodecRuntime.NONE);
|
||||||
}
|
const [adrAlgorithms, setAdrAlgorithms] = useState<[string, string][]>([]);
|
||||||
|
|
||||||
class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
formRef = React.createRef<any>();
|
const v = props.initialValues;
|
||||||
|
setSupportsOtaa(v.getSupportsOtaa());
|
||||||
constructor(props: IProps) {
|
setSupportsClassB(v.getSupportsClassB());
|
||||||
super(props);
|
setSupportsClassC(v.getSupportsClassC());
|
||||||
this.state = {
|
setPayloadCodecRuntime(v.getPayloadCodecRuntime());
|
||||||
supportsOtaa: false,
|
|
||||||
supportsClassB: false,
|
|
||||||
supportsClassC: false,
|
|
||||||
payloadCodecRuntime: CodecRuntime.NONE,
|
|
||||||
adrAlgorithms: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const v = this.props.initialValues;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
supportsOtaa: v.getSupportsOtaa(),
|
|
||||||
supportsClassB: v.getSupportsClassB(),
|
|
||||||
supportsClassC: v.getSupportsClassC(),
|
|
||||||
payloadCodecRuntime: v.getPayloadCodecRuntime(),
|
|
||||||
});
|
|
||||||
|
|
||||||
DeviceProfileStore.listAdrAlgorithms((resp: ListDeviceProfileAdrAlgorithmsResponse) => {
|
DeviceProfileStore.listAdrAlgorithms((resp: ListDeviceProfileAdrAlgorithmsResponse) => {
|
||||||
let adrAlgorithms: [string, string][] = [];
|
let adrAlgorithms: [string, string][] = [];
|
||||||
@ -55,14 +38,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
adrAlgorithms.push([a.getId(), a.getName()]);
|
adrAlgorithms.push([a.getId(), a.getName()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setAdrAlgorithms(adrAlgorithms);
|
||||||
adrAlgorithms: adrAlgorithms,
|
|
||||||
});
|
});
|
||||||
});
|
}, [props.initialValues]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (values: DeviceProfileTemplate.AsObject) => {
|
const onFinish = (values: DeviceProfileTemplate.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let dp = new DeviceProfileTemplate();
|
let dp = new DeviceProfileTemplate();
|
||||||
dp.setId(v.id);
|
dp.setId(v.id);
|
||||||
|
|
||||||
@ -114,43 +95,29 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
dp.setAutoDetectMeasurements(v.autoDetectMeasurements);
|
dp.setAutoDetectMeasurements(v.autoDetectMeasurements);
|
||||||
|
|
||||||
this.props.onFinish(dp);
|
props.onFinish(dp);
|
||||||
};
|
};
|
||||||
|
|
||||||
onSupportsOtaaChange = (checked: boolean) => {
|
const onSupportsOtaaChange = (checked: boolean) => {
|
||||||
this.setState({
|
setSupportsOtaa(checked);
|
||||||
supportsOtaa: checked,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onSupportsClassBChnage = (checked: boolean) => {
|
const onSupportsClassBChnage = (checked: boolean) => {
|
||||||
this.setState({
|
setSupportsClassB(checked);
|
||||||
supportsClassB: checked,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onSupportsClassCChange = (checked: boolean) => {
|
const onSupportsClassCChange = (checked: boolean) => {
|
||||||
this.setState({
|
setSupportsClassC(checked);
|
||||||
supportsClassC: checked,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onPayloadCodecRuntimeChange = (value: CodecRuntime) => {
|
const onPayloadCodecRuntimeChange = (value: CodecRuntime) => {
|
||||||
this.setState({
|
setPayloadCodecRuntime(value);
|
||||||
payloadCodecRuntime: value,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const adrOptions = adrAlgorithms.map(v => <Select.Option value={v[0]}>{v[1]}</Select.Option>);
|
||||||
const adrOptions = this.state.adrAlgorithms.map(v => <Select.Option value={v[0]}>{v[1]}</Select.Option>);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish} form={form}>
|
||||||
layout="vertical"
|
|
||||||
initialValues={this.props.initialValues.toObject()}
|
|
||||||
onFinish={this.onFinish}
|
|
||||||
ref={this.formRef}
|
|
||||||
>
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tabs.TabPane tab="General" key="1">
|
<Tabs.TabPane tab="General" key="1">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -164,7 +131,7 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={!!this.props.update} />
|
<Input disabled={!!props.update} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
||||||
<Input />
|
<Input />
|
||||||
@ -222,7 +189,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
label="Regional parameters revision"
|
label="Regional parameters revision"
|
||||||
tooltip="Revision of the Regional Parameters specification supported by the device."
|
tooltip="Revision of the Regional Parameters specification supported by the device."
|
||||||
name="regParamsRevision"
|
name="regParamsRevision"
|
||||||
rules={[{ required: true, message: "Please select a regional parameters revision!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please select a regional parameters revision!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Select>
|
<Select>
|
||||||
<Select.Option value={RegParamsRevision.A}>A</Select.Option>
|
<Select.Option value={RegParamsRevision.A}>A</Select.Option>
|
||||||
@ -259,7 +231,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
label="Expected uplink interval (secs)"
|
label="Expected uplink interval (secs)"
|
||||||
tooltip="The expected interval in seconds in which the device sends uplink messages. This is used to determine if a device is active or inactive."
|
tooltip="The expected interval in seconds in which the device sends uplink messages. This is used to determine if a device is active or inactive."
|
||||||
name="uplinkInterval"
|
name="uplinkInterval"
|
||||||
rules={[{ required: true, message: "Please enter an uplink interval!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter an uplink interval!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -277,9 +254,9 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="Join (OTAA / ABP)" key="2">
|
<Tabs.TabPane tab="Join (OTAA / ABP)" key="2">
|
||||||
<Form.Item label="Device supports OTAA" name="supportsOtaa" valuePropName="checked">
|
<Form.Item label="Device supports OTAA" name="supportsOtaa" valuePropName="checked">
|
||||||
<Switch onChange={this.onSupportsOtaaChange} />
|
<Switch onChange={onSupportsOtaaChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{!this.state.supportsOtaa && (
|
{!supportsOtaa && (
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -295,21 +272,31 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
label="RX1 data-rate offset"
|
label="RX1 data-rate offset"
|
||||||
tooltip="Please refer the LoRaWAN Regional Parameters specification for valid values."
|
tooltip="Please refer the LoRaWAN Regional Parameters specification for valid values."
|
||||||
name="abpRx1DrOffset"
|
name="abpRx1DrOffset"
|
||||||
rules={[{ required: true, message: "Please enter a RX1 data-rate offset!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter a RX1 data-rate offset!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={15} />
|
<InputNumber min={0} max={15} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{!this.state.supportsOtaa && (
|
{!supportsOtaa && (
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="RX2 data-rate"
|
label="RX2 data-rate"
|
||||||
tooltip="Please refer the LoRaWAN Regional Parameters specification for valid values."
|
tooltip="Please refer the LoRaWAN Regional Parameters specification for valid values."
|
||||||
name="abpRx2Dr"
|
name="abpRx2Dr"
|
||||||
rules={[{ required: true, message: "Please enter a RX2 data-rate!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter a RX2 data-rate!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={15} />
|
<InputNumber min={0} max={15} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -318,7 +305,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
label="RX2 frequency (Hz)"
|
label="RX2 frequency (Hz)"
|
||||||
name="abpRx2Freq"
|
name="abpRx2Freq"
|
||||||
rules={[{ required: true, message: "Please enter a RX2 frequency!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter a RX2 frequency!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} style={{ width: "200px" }} />
|
<InputNumber min={0} style={{ width: "200px" }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -328,9 +320,9 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="Class-B" key="3">
|
<Tabs.TabPane tab="Class-B" key="3">
|
||||||
<Form.Item label="Device supports Class-B" name="supportsClassB" valuePropName="checked">
|
<Form.Item label="Device supports Class-B" name="supportsClassB" valuePropName="checked">
|
||||||
<Switch onChange={this.onSupportsClassBChnage} />
|
<Switch onChange={onSupportsClassBChnage} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{this.state.supportsClassB && (
|
{supportsClassB && (
|
||||||
<>
|
<>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
@ -338,7 +330,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
label="Class-B confirmed downlink timeout (seconds)"
|
label="Class-B confirmed downlink timeout (seconds)"
|
||||||
tooltip="Class-B timeout (in seconds) for confirmed downlink transmissions."
|
tooltip="Class-B timeout (in seconds) for confirmed downlink transmissions."
|
||||||
name="classBTimeout"
|
name="classBTimeout"
|
||||||
rules={[{ required: true, message: "Please enter a Class-B confirmed downlink timeout!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter a Class-B confirmed downlink timeout!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -348,7 +345,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
label="Class-B ping-slot periodicity"
|
label="Class-B ping-slot periodicity"
|
||||||
tooltip="This value must match the ping-slot periodicity of the device. Please refer to the device documentation."
|
tooltip="This value must match the ping-slot periodicity of the device. Please refer to the device documentation."
|
||||||
name="classBPingSlotNbK"
|
name="classBPingSlotNbK"
|
||||||
rules={[{ required: true, message: "Please select the ping-slot periodicity!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please select the ping-slot periodicity!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Select>
|
<Select>
|
||||||
<Select.Option value={0}>Every second</Select.Option>
|
<Select.Option value={0}>Every second</Select.Option>
|
||||||
@ -369,7 +371,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
label="Class-B ping-slot data-rate"
|
label="Class-B ping-slot data-rate"
|
||||||
tooltip="This value must match the ping-slot data-rate of the device. Please refer to the device documentation."
|
tooltip="This value must match the ping-slot data-rate of the device. Please refer to the device documentation."
|
||||||
name="classBPingSlotDr"
|
name="classBPingSlotDr"
|
||||||
rules={[{ required: true, message: "Please enter the ping-slot data-rate!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter the ping-slot data-rate!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -379,7 +386,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
label="Class-B ping-slot frequency (Hz)"
|
label="Class-B ping-slot frequency (Hz)"
|
||||||
tooltip="This value must match the ping-slot frequency of the device. Please refer to the device documentation."
|
tooltip="This value must match the ping-slot frequency of the device. Please refer to the device documentation."
|
||||||
name="classBPingSlotFreq"
|
name="classBPingSlotFreq"
|
||||||
rules={[{ required: true, message: "Please enter the ping-slot frequency!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter the ping-slot frequency!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} style={{ width: "200px" }} />
|
<InputNumber min={0} style={{ width: "200px" }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -390,14 +402,19 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="Class-C" key="4">
|
<Tabs.TabPane tab="Class-C" key="4">
|
||||||
<Form.Item label="Device supports Class-C" name="supportsClassC" valuePropName="checked">
|
<Form.Item label="Device supports Class-C" name="supportsClassC" valuePropName="checked">
|
||||||
<Switch onChange={this.onSupportsClassCChange} />
|
<Switch onChange={onSupportsClassCChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{this.state.supportsClassC && (
|
{supportsClassC && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Class-C confirmed downlink timeout (seconds)"
|
label="Class-C confirmed downlink timeout (seconds)"
|
||||||
tooltip="Class-C timeout (in seconds) for confirmed downlink transmissions."
|
tooltip="Class-C timeout (in seconds) for confirmed downlink transmissions."
|
||||||
name="classCTimeout"
|
name="classCTimeout"
|
||||||
rules={[{ required: true, message: "Please enter a Class-C confirmed downlink timeout!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter a Class-C confirmed downlink timeout!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -409,20 +426,13 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
name="payloadCodecRuntime"
|
name="payloadCodecRuntime"
|
||||||
tooltip="By defining a payload codec, ChirpStack Application Server can encode and decode the binary device payload for you."
|
tooltip="By defining a payload codec, ChirpStack Application Server can encode and decode the binary device payload for you."
|
||||||
>
|
>
|
||||||
<Select onChange={this.onPayloadCodecRuntimeChange}>
|
<Select onChange={onPayloadCodecRuntimeChange}>
|
||||||
<Select.Option value={CodecRuntime.NONE}>None</Select.Option>
|
<Select.Option value={CodecRuntime.NONE}>None</Select.Option>
|
||||||
<Select.Option value={CodecRuntime.CAYENNE_LPP}>Cayenne LPP</Select.Option>
|
<Select.Option value={CodecRuntime.CAYENNE_LPP}>Cayenne LPP</Select.Option>
|
||||||
<Select.Option value={CodecRuntime.JS}>JavaScript functions</Select.Option>
|
<Select.Option value={CodecRuntime.JS}>JavaScript functions</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{this.state.payloadCodecRuntime === CodecRuntime.JS && (
|
{payloadCodecRuntime === CodecRuntime.JS && <CodeEditor label="Codec functions" name="payloadCodecScript" />}
|
||||||
<CodeEditor
|
|
||||||
label="Codec functions"
|
|
||||||
name="payloadCodecScript"
|
|
||||||
value={this.formRef.current.getFieldValue("payloadCodecScript")}
|
|
||||||
formRef={this.formRef}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="Tags" key="6">
|
<Tabs.TabPane tab="Tags" key="6">
|
||||||
<Form.List name="tagsMap">
|
<Form.List name="tagsMap">
|
||||||
@ -473,8 +483,8 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<strong>Unknown / unset</strong>: Default for auto-detected keys. This disables the aggregation of
|
<strong>Unknown / unset</strong>: Default for auto-detected keys. This disables the aggregation of this
|
||||||
this metric.
|
metric.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Counter</strong>: For continuous incrementing counters.
|
<strong>Counter</strong>: For continuous incrementing counters.
|
||||||
@ -534,7 +544,12 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
{...restField}
|
{...restField}
|
||||||
name={[name, 1, "name"]}
|
name={[name, 1, "name"]}
|
||||||
fieldKey={[name, 1, "name"]}
|
fieldKey={[name, 1, "name"]}
|
||||||
rules={[{ required: true, message: "Please enter a description!" }]}
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "Please enter a description!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input placeholder="Name" />
|
<Input placeholder="Name" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -562,6 +577,5 @@ class DeviceProfileTemplateForm extends Component<IProps, IState> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DeviceProfileTemplateForm;
|
export default DeviceProfileTemplateForm;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps, Link } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, Button, PageHeader } from "antd";
|
import { useParams, Link, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
import { Space, Breadcrumb, Card, Button } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DeviceProfileTemplate,
|
DeviceProfileTemplate,
|
||||||
@ -15,58 +17,40 @@ import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
|
|||||||
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
|
|
||||||
interface IState {
|
function EditDeviceProfileTemplate() {
|
||||||
deviceProfileTemplate?: DeviceProfileTemplate;
|
const navigate = useNavigate();
|
||||||
}
|
const [deviceProfileTemplate, setDeviceProfileTemplate] = useState<DeviceProfileTemplate | undefined>(undefined);
|
||||||
|
const { deviceProfileTemplateId } = useParams();
|
||||||
|
|
||||||
interface MatchParams {
|
useEffect(() => {
|
||||||
deviceProfileTemplateId: string;
|
const id = deviceProfileTemplateId!;
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps<MatchParams> {}
|
|
||||||
|
|
||||||
class EditDeviceProfileTemplate extends Component<IProps, IState> {
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.getDeviceProfileTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeviceProfileTemplate = () => {
|
|
||||||
const id = this.props.match.params.deviceProfileTemplateId;
|
|
||||||
let req = new GetDeviceProfileTemplateRequest();
|
let req = new GetDeviceProfileTemplateRequest();
|
||||||
req.setId(id);
|
req.setId(id);
|
||||||
|
|
||||||
DeviceProfileTemplateStore.get(req, (resp: GetDeviceProfileTemplateResponse) => {
|
DeviceProfileTemplateStore.get(req, (resp: GetDeviceProfileTemplateResponse) => {
|
||||||
this.setState({
|
setDeviceProfileTemplate(resp.getDeviceProfileTemplate());
|
||||||
deviceProfileTemplate: resp.getDeviceProfileTemplate(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [deviceProfileTemplateId]);
|
||||||
};
|
|
||||||
|
|
||||||
onFinish = (obj: DeviceProfileTemplate) => {
|
const onFinish = (obj: DeviceProfileTemplate) => {
|
||||||
let req = new UpdateDeviceProfileTemplateRequest();
|
let req = new UpdateDeviceProfileTemplateRequest();
|
||||||
req.setDeviceProfileTemplate(obj);
|
req.setDeviceProfileTemplate(obj);
|
||||||
|
|
||||||
DeviceProfileTemplateStore.update(req, () => {
|
DeviceProfileTemplateStore.update(req, () => {
|
||||||
this.props.history.push(`/device-profile-templates`);
|
navigate(`/device-profile-templates`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteDeviceProfileTemplate = () => {
|
const deleteDeviceProfileTemplate = () => {
|
||||||
let req = new DeleteDeviceProfileTemplateRequest();
|
let req = new DeleteDeviceProfileTemplateRequest();
|
||||||
req.setId(this.props.match.params.deviceProfileTemplateId);
|
req.setId(deviceProfileTemplateId!);
|
||||||
|
|
||||||
DeviceProfileTemplateStore.delete(req, () => {
|
DeviceProfileTemplateStore.delete(req, () => {
|
||||||
this.props.history.push(`/device-profile-templates`);
|
navigate(`/device-profile-templates`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const dp = deviceProfileTemplate;
|
||||||
const dp = this.state.deviceProfileTemplate;
|
|
||||||
|
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
return null;
|
return null;
|
||||||
@ -93,11 +77,7 @@ class EditDeviceProfileTemplate extends Component<IProps, IState> {
|
|||||||
title={dp.getName()}
|
title={dp.getName()}
|
||||||
subTitle={`device-profile template id: ${dp.getId()}`}
|
subTitle={`device-profile template id: ${dp.getId()}`}
|
||||||
extra={[
|
extra={[
|
||||||
<DeleteConfirm
|
<DeleteConfirm typ="device-profile template" confirm={dp.getName()} onConfirm={deleteDeviceProfileTemplate}>
|
||||||
typ="device-profile template"
|
|
||||||
confirm={dp.getName()}
|
|
||||||
onConfirm={this.deleteDeviceProfileTemplate}
|
|
||||||
>
|
|
||||||
<Button danger type="primary">
|
<Button danger type="primary">
|
||||||
Delete device-profile template
|
Delete device-profile template
|
||||||
</Button>
|
</Button>
|
||||||
@ -105,11 +85,10 @@ class EditDeviceProfileTemplate extends Component<IProps, IState> {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<DeviceProfileTemplateForm initialValues={dp} update={true} onFinish={this.onFinish} />
|
<DeviceProfileTemplateForm initialValues={dp} update={true} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditDeviceProfileTemplate;
|
export default EditDeviceProfileTemplate;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
import { Space, Breadcrumb, Button } from "antd";
|
||||||
import { ColumnsType } from "antd/es/table";
|
import { ColumnsType } from "antd/es/table";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ListDeviceProfileTemplatesRequest,
|
ListDeviceProfileTemplatesRequest,
|
||||||
@ -15,9 +15,8 @@ import { getEnumName } from "../helpers";
|
|||||||
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
||||||
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
||||||
|
|
||||||
class ListDeviceProfileTemplates extends Component {
|
function ListDeviceProfileTemplates() {
|
||||||
columns = (): ColumnsType<DeviceProfileTemplateListItem.AsObject> => {
|
const columns: ColumnsType<DeviceProfileTemplateListItem.AsObject> = [
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
title: "Vendor",
|
title: "Vendor",
|
||||||
dataIndex: "vendor",
|
dataIndex: "vendor",
|
||||||
@ -44,9 +43,8 @@ class ListDeviceProfileTemplates extends Component {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
const getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||||
let req = new ListDeviceProfileTemplatesRequest();
|
let req = new ListDeviceProfileTemplatesRequest();
|
||||||
req.setLimit(limit);
|
req.setLimit(limit);
|
||||||
req.setOffset(offset);
|
req.setOffset(offset);
|
||||||
@ -57,7 +55,6 @@ class ListDeviceProfileTemplates extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
@ -78,10 +75,9 @@ class ListDeviceProfileTemplates extends Component {
|
|||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" />
|
<DataTable columns={columns} getPage={getPage} rowKey="id" />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListDeviceProfileTemplates;
|
export default ListDeviceProfileTemplates;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { Link, RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
import { Space, Breadcrumb, Card } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
||||||
import {
|
import {
|
||||||
@ -15,23 +15,24 @@ import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
|||||||
import DeviceProfileForm from "./DeviceProfileForm";
|
import DeviceProfileForm from "./DeviceProfileForm";
|
||||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateDeviceProfile extends Component<IProps> {
|
function CreateDeviceProfile(props: IProps) {
|
||||||
onFinish = (obj: DeviceProfile) => {
|
const navigate = useNavigate();
|
||||||
obj.setTenantId(this.props.tenant.getId());
|
|
||||||
|
const onFinish = (obj: DeviceProfile) => {
|
||||||
|
obj.setTenantId(props.tenant.getId());
|
||||||
|
|
||||||
let req = new CreateDeviceProfileRequest();
|
let req = new CreateDeviceProfileRequest();
|
||||||
req.setDeviceProfile(obj);
|
req.setDeviceProfile(obj);
|
||||||
|
|
||||||
DeviceProfileStore.create(req, (_resp: CreateDeviceProfileResponse) => {
|
DeviceProfileStore.create(req, (_resp: CreateDeviceProfileResponse) => {
|
||||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
|
navigate(`/tenants/${props.tenant.getId()}/device-profiles`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const codecScript = `// Decode uplink function.
|
const codecScript = `// Decode uplink function.
|
||||||
//
|
//
|
||||||
// Input is an object with the following fields:
|
// Input is an object with the following fields:
|
||||||
@ -85,12 +86,12 @@ function encodeDownlink(input) {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/device-profiles`}>Device profiles</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/device-profiles`}>Device profiles</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -101,11 +102,10 @@ function encodeDownlink(input) {
|
|||||||
title="Add device profile"
|
title="Add device profile"
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<DeviceProfileForm initialValues={deviceProfile} onFinish={this.onFinish} />
|
<DeviceProfileForm initialValues={deviceProfile} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateDeviceProfile;
|
export default CreateDeviceProfile;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps, Link } from "react-router-dom";
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, Button, PageHeader } from "antd";
|
import { Space, Breadcrumb, Card, Button } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import {
|
import {
|
||||||
@ -18,60 +19,44 @@ import SessionStore from "../../stores/SessionStore";
|
|||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
|
||||||
interface IState {
|
interface IProps {
|
||||||
deviceProfile?: DeviceProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MatchParams {
|
|
||||||
deviceProfileId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps<MatchParams> {
|
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditDeviceProfile extends Component<IProps, IState> {
|
function EditDeviceProfile(props: IProps) {
|
||||||
constructor(props: IProps) {
|
const navigate = useNavigate();
|
||||||
super(props);
|
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
|
||||||
this.state = {};
|
const { deviceProfileId } = useParams();
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
useEffect(() => {
|
||||||
this.getDeviceProfile();
|
const id = deviceProfileId!;
|
||||||
}
|
|
||||||
|
|
||||||
getDeviceProfile = () => {
|
|
||||||
const id = this.props.match.params.deviceProfileId;
|
|
||||||
let req = new GetDeviceProfileRequest();
|
let req = new GetDeviceProfileRequest();
|
||||||
req.setId(id);
|
req.setId(id);
|
||||||
|
|
||||||
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
||||||
this.setState({
|
setDeviceProfile(resp.getDeviceProfile());
|
||||||
deviceProfile: resp.getDeviceProfile(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [deviceProfileId]);
|
||||||
};
|
|
||||||
|
|
||||||
onFinish = (obj: DeviceProfile) => {
|
const onFinish = (obj: DeviceProfile) => {
|
||||||
let req = new UpdateDeviceProfileRequest();
|
let req = new UpdateDeviceProfileRequest();
|
||||||
req.setDeviceProfile(obj);
|
req.setDeviceProfile(obj);
|
||||||
|
|
||||||
DeviceProfileStore.update(req, () => {
|
DeviceProfileStore.update(req, () => {
|
||||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
|
navigate(`/tenants/${props.tenant.getId()}/device-profiles`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteDeviceProfile = () => {
|
const deleteDeviceProfile = () => {
|
||||||
let req = new DeleteDeviceProfileRequest();
|
let req = new DeleteDeviceProfileRequest();
|
||||||
req.setId(this.props.match.params.deviceProfileId);
|
req.setId(deviceProfileId!);
|
||||||
|
|
||||||
DeviceProfileStore.delete(req, () => {
|
DeviceProfileStore.delete(req, () => {
|
||||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
|
navigate(`/tenants/${props.tenant.getId()}/device-profiles`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const dp = deviceProfile;
|
||||||
const dp = this.state.deviceProfile;
|
|
||||||
|
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
return null;
|
return null;
|
||||||
@ -79,8 +64,8 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
|||||||
|
|
||||||
const disabled = !(
|
const disabled = !(
|
||||||
SessionStore.isAdmin() ||
|
SessionStore.isAdmin() ||
|
||||||
SessionStore.isTenantAdmin(this.props.tenant.getId()) ||
|
SessionStore.isTenantAdmin(props.tenant.getId()) ||
|
||||||
SessionStore.isTenantDeviceAdmin(this.props.tenant.getId())
|
SessionStore.isTenantDeviceAdmin(props.tenant.getId())
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -93,12 +78,12 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/device-profiles`}>Device profiles</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/device-profiles`}>Device profiles</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -109,8 +94,8 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
|||||||
title={dp.getName()}
|
title={dp.getName()}
|
||||||
subTitle={`device profile id: ${dp.getId()}`}
|
subTitle={`device profile id: ${dp.getId()}`}
|
||||||
extra={[
|
extra={[
|
||||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
<Admin tenantId={props.tenant.getId()} isDeviceAdmin>
|
||||||
<DeleteConfirm typ="device profile" confirm={dp.getName()} onConfirm={this.deleteDeviceProfile}>
|
<DeleteConfirm typ="device profile" confirm={dp.getName()} onConfirm={deleteDeviceProfile}>
|
||||||
<Button danger type="primary">
|
<Button danger type="primary">
|
||||||
Delete device profile
|
Delete device profile
|
||||||
</Button>
|
</Button>
|
||||||
@ -119,11 +104,10 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<DeviceProfileForm initialValues={dp} disabled={disabled} onFinish={this.onFinish} />
|
<DeviceProfileForm initialValues={dp} disabled={disabled} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default EditDeviceProfile;
|
export default EditDeviceProfile;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
import { Space, Breadcrumb, Button } from "antd";
|
||||||
import { ColumnsType } from "antd/es/table";
|
import { ColumnsType } from "antd/es/table";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ListDeviceProfilesRequest,
|
ListDeviceProfilesRequest,
|
||||||
@ -21,15 +21,14 @@ interface IProps {
|
|||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListDeviceProfiles extends Component<IProps> {
|
function ListDeviceProfiles(props: IProps) {
|
||||||
columns = (): ColumnsType<DeviceProfileListItem.AsObject> => {
|
const columns: ColumnsType<DeviceProfileListItem.AsObject> = [
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
title: "Name",
|
title: "Name",
|
||||||
dataIndex: "name",
|
dataIndex: "name",
|
||||||
key: "name",
|
key: "name",
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/device-profiles/${record.id}/edit`}>{text}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/device-profiles/${record.id}/edit`}>{text}</Link>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -99,11 +98,10 @@ class ListDeviceProfiles extends Component<IProps> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
const getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||||
let req = new ListDeviceProfilesRequest();
|
let req = new ListDeviceProfilesRequest();
|
||||||
req.setTenantId(this.props.tenant.getId());
|
req.setTenantId(props.tenant.getId());
|
||||||
req.setLimit(limit);
|
req.setLimit(limit);
|
||||||
req.setOffset(offset);
|
req.setOffset(offset);
|
||||||
|
|
||||||
@ -113,7 +111,6 @@ class ListDeviceProfiles extends Component<IProps> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
@ -124,7 +121,7 @@ class ListDeviceProfiles extends Component<IProps> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -134,17 +131,16 @@ class ListDeviceProfiles extends Component<IProps> {
|
|||||||
)}
|
)}
|
||||||
title="Device profiles"
|
title="Device profiles"
|
||||||
extra={[
|
extra={[
|
||||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
<Admin tenantId={props.tenant.getId()} isDeviceAdmin>
|
||||||
<Button type="primary">
|
<Button type="primary">
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/device-profiles/create`}>Add device profile</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/device-profiles/create`}>Add device profile</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</Admin>,
|
</Admin>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" />
|
<DataTable columns={columns} getPage={getPage} rowKey="id" />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListDeviceProfiles;
|
export default ListDeviceProfiles;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from "react";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { Link, RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
import { Space, Breadcrumb, Card } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -15,14 +15,16 @@ import DeviceForm from "./DeviceForm";
|
|||||||
import DeviceStore from "../../stores/DeviceStore";
|
import DeviceStore from "../../stores/DeviceStore";
|
||||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateDevice extends Component<IProps> {
|
function CreateDevice(props: IProps) {
|
||||||
onFinish = (obj: Device) => {
|
const navigate = useNavigate();
|
||||||
obj.setApplicationId(this.props.application.getId());
|
|
||||||
|
const onFinish = (obj: Device) => {
|
||||||
|
obj.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
let req = new CreateDeviceRequest();
|
let req = new CreateDeviceRequest();
|
||||||
req.setDevice(obj);
|
req.setDevice(obj);
|
||||||
@ -34,21 +36,20 @@ class CreateDevice extends Component<IProps> {
|
|||||||
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
||||||
let dp = resp.getDeviceProfile()!;
|
let dp = resp.getDeviceProfile()!;
|
||||||
if (dp.getSupportsOtaa()) {
|
if (dp.getSupportsOtaa()) {
|
||||||
this.props.history.push(
|
navigate(
|
||||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}/keys`,
|
`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}/devices/${obj.getDevEui()}/keys`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(
|
navigate(
|
||||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}`,
|
`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}/devices/${obj.getDevEui()}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
let device = new Device();
|
let device = new Device();
|
||||||
device.setApplicationId(this.props.application.getId());
|
device.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
@ -60,18 +61,18 @@ class CreateDevice extends Component<IProps> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/applications`}>Applications</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>
|
<Link to={`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}`}>
|
||||||
{this.props.application.getName()}
|
{props.application.getName()}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
@ -83,11 +84,10 @@ class CreateDevice extends Component<IProps> {
|
|||||||
title="Add device"
|
title="Add device"
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<DeviceForm tenant={this.props.tenant} initialValues={device} onFinish={this.onFinish} />
|
<DeviceForm tenant={props.tenant} initialValues={device} onFinish={onFinish} />
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateDevice;
|
export default CreateDevice;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Form, Button, Row, Col, InputNumber, Alert } from "antd";
|
import { Space, Form, Button, Row, Col, InputNumber, Alert } from "antd";
|
||||||
|
|
||||||
@ -26,11 +26,11 @@ interface FormProps {
|
|||||||
onFinish: (obj: DeviceActivationPb) => void;
|
onFinish: (obj: DeviceActivationPb) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class LW10DeviceActivationForm extends Component<FormProps> {
|
function LW10DeviceActivationForm(props: FormProps) {
|
||||||
formRef = React.createRef<any>();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
onFinish = (values: DeviceActivationPb.AsObject) => {
|
const onFinish = (values: DeviceActivationPb.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let da = new DeviceActivationPb();
|
let da = new DeviceActivationPb();
|
||||||
|
|
||||||
da.setDevAddr(v.devAddr);
|
da.setDevAddr(v.devAddr);
|
||||||
@ -42,37 +42,28 @@ class LW10DeviceActivationForm extends Component<FormProps> {
|
|||||||
da.setAFCntDown(v.nFCntDown);
|
da.setAFCntDown(v.nFCntDown);
|
||||||
da.setNFCntDown(v.nFCntDown);
|
da.setNFCntDown(v.nFCntDown);
|
||||||
|
|
||||||
this.props.onFinish(da);
|
props.onFinish(da);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish} form={form}>
|
||||||
layout="vertical"
|
|
||||||
initialValues={this.props.initialValues.toObject()}
|
|
||||||
onFinish={this.onFinish}
|
|
||||||
ref={this.formRef}
|
|
||||||
>
|
|
||||||
<DevAddrInput
|
<DevAddrInput
|
||||||
label="Device address"
|
label="Device address"
|
||||||
name="devAddr"
|
name="devAddr"
|
||||||
value={this.props.initialValues.getDevAddr()}
|
value={props.initialValues.getDevAddr()}
|
||||||
devEui={this.props.device.getDevEui()}
|
devEui={props.device.getDevEui()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Network session key (LoRaWAN 1.0)"
|
label="Network session key (LoRaWAN 1.0)"
|
||||||
name="nwkSEncKey"
|
name="nwkSEncKey"
|
||||||
value={this.props.initialValues.getNwkSEncKey()}
|
value={props.initialValues.getNwkSEncKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Application session key (LoRaWAN 1.0)"
|
label="Application session key (LoRaWAN 1.0)"
|
||||||
name="appSKey"
|
name="appSKey"
|
||||||
value={this.props.initialValues.getAppSKey()}
|
value={props.initialValues.getAppSKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
@ -88,20 +79,19 @@ class LW10DeviceActivationForm extends Component<FormProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" htmlType="submit" disabled={this.props.disabled}>
|
<Button type="primary" htmlType="submit" disabled={props.disabled}>
|
||||||
(Re)activate device
|
(Re)activate device
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class LW11DeviceActivationForm extends Component<FormProps> {
|
function LW11DeviceActivationForm(props: FormProps) {
|
||||||
formRef = React.createRef<any>();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
onFinish = (values: DeviceActivationPb.AsObject) => {
|
const onFinish = (values: DeviceActivationPb.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let da = new DeviceActivationPb();
|
let da = new DeviceActivationPb();
|
||||||
|
|
||||||
da.setDevAddr(v.devAddr);
|
da.setDevAddr(v.devAddr);
|
||||||
@ -113,53 +103,37 @@ class LW11DeviceActivationForm extends Component<FormProps> {
|
|||||||
da.setAFCntDown(v.aFCntDown);
|
da.setAFCntDown(v.aFCntDown);
|
||||||
da.setNFCntDown(v.nFCntDown);
|
da.setNFCntDown(v.nFCntDown);
|
||||||
|
|
||||||
this.props.onFinish(da);
|
props.onFinish(da);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish} form={form}>
|
||||||
layout="vertical"
|
|
||||||
initialValues={this.props.initialValues.toObject()}
|
|
||||||
onFinish={this.onFinish}
|
|
||||||
ref={this.formRef}
|
|
||||||
>
|
|
||||||
<DevAddrInput
|
<DevAddrInput
|
||||||
label="Device address"
|
label="Device address"
|
||||||
name="devAddr"
|
name="devAddr"
|
||||||
value={this.props.initialValues.getDevAddr()}
|
value={props.initialValues.getDevAddr()}
|
||||||
devEui={this.props.device.getDevEui()}
|
devEui={props.device.getDevEui()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Network session encryption key"
|
label="Network session encryption key"
|
||||||
name="nwkSEncKey"
|
name="nwkSEncKey"
|
||||||
value={this.props.initialValues.getNwkSEncKey()}
|
value={props.initialValues.getNwkSEncKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Serving network session integrity key"
|
label="Serving network session integrity key"
|
||||||
name="sNwkSIntKey"
|
name="sNwkSIntKey"
|
||||||
value={this.props.initialValues.getSNwkSIntKey()}
|
value={props.initialValues.getSNwkSIntKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Forwarding network session integrity key"
|
label="Forwarding network session integrity key"
|
||||||
name="fNwkSIntKey"
|
name="fNwkSIntKey"
|
||||||
value={this.props.initialValues.getFNwkSIntKey()}
|
value={props.initialValues.getFNwkSIntKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<AesKeyInput
|
|
||||||
label="Application session key"
|
|
||||||
name="appSKey"
|
|
||||||
value={this.props.initialValues.getAppSKey()}
|
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<AesKeyInput label="Application session key" name="appSKey" value={props.initialValues.getAppSKey()} required />
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={6}>
|
<Col span={6}>
|
||||||
<Form.Item label="Uplink frame-counter" name="fCntUp">
|
<Form.Item label="Uplink frame-counter" name="fCntUp">
|
||||||
@ -178,74 +152,62 @@ class LW11DeviceActivationForm extends Component<FormProps> {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" htmlType="submit" disabled={this.props.disabled}>
|
<Button type="primary" htmlType="submit" disabled={props.disabled}>
|
||||||
(Re)activate device
|
(Re)activate device
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
application: Application;
|
application: Application;
|
||||||
device: Device;
|
device: Device;
|
||||||
deviceProfile: DeviceProfile;
|
deviceProfile: DeviceProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DeviceActivation(props: IProps) {
|
||||||
deviceActivation?: DeviceActivationPb;
|
const navigate = useNavigate();
|
||||||
deviceActivationRequested: boolean;
|
const [deviceActivation, setDeviceActivation] = useState<DeviceActivationPb | undefined>(undefined);
|
||||||
}
|
const [deviceActivationRequested, setDeviceActivationRequested] = useState<boolean>(false);
|
||||||
|
|
||||||
class DeviceActivation extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
deviceActivationRequested: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let req = new GetDeviceActivationRequest();
|
let req = new GetDeviceActivationRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
|
|
||||||
DeviceStore.getActivation(req, (resp: GetDeviceActivationResponse) => {
|
DeviceStore.getActivation(req, (resp: GetDeviceActivationResponse) => {
|
||||||
this.setState({
|
setDeviceActivation(resp.getDeviceActivation());
|
||||||
deviceActivation: resp.getDeviceActivation(),
|
setDeviceActivationRequested(true);
|
||||||
deviceActivationRequested: true,
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
onFinish = (obj: DeviceActivationPb) => {
|
const onFinish = (obj: DeviceActivationPb) => {
|
||||||
let req = new ActivateDeviceRequest();
|
let req = new ActivateDeviceRequest();
|
||||||
obj.setDevEui(this.props.device.getDevEui());
|
obj.setDevEui(props.device.getDevEui());
|
||||||
req.setDeviceActivation(obj);
|
req.setDeviceActivation(obj);
|
||||||
|
|
||||||
DeviceStore.activate(req, () => {
|
DeviceStore.activate(req, () => {
|
||||||
this.props.history.push(
|
navigate(
|
||||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${this.props.device.getDevEui()}`,
|
`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}/devices/${props.device.getDevEui()}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (!deviceActivationRequested) {
|
||||||
if (!this.state.deviceActivationRequested) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.state.deviceActivation && this.props.deviceProfile.getSupportsOtaa()) {
|
if (!deviceActivation && props.deviceProfile.getSupportsOtaa()) {
|
||||||
return <Alert type="info" showIcon message="This device has not (yet) been activated." />;
|
return <Alert type="info" showIcon message="This device has not (yet) been activated." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let macVersion = this.props.deviceProfile.getMacVersion();
|
let macVersion = props.deviceProfile.getMacVersion();
|
||||||
const lw11 = macVersion === MacVersion.LORAWAN_1_1_0;
|
const lw11 = macVersion === MacVersion.LORAWAN_1_1_0;
|
||||||
|
|
||||||
let initialValues = new DeviceActivationPb();
|
let initialValues = new DeviceActivationPb();
|
||||||
if (this.state.deviceActivation) {
|
if (deviceActivation) {
|
||||||
initialValues = this.state.deviceActivation;
|
initialValues = deviceActivation;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -253,22 +215,21 @@ class DeviceActivation extends Component<IProps, IState> {
|
|||||||
{!lw11 && (
|
{!lw11 && (
|
||||||
<LW10DeviceActivationForm
|
<LW10DeviceActivationForm
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
device={this.props.device}
|
device={props.device}
|
||||||
onFinish={this.onFinish}
|
onFinish={onFinish}
|
||||||
disabled={this.props.deviceProfile.getSupportsOtaa()}
|
disabled={props.deviceProfile.getSupportsOtaa()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{lw11 && (
|
{lw11 && (
|
||||||
<LW11DeviceActivationForm
|
<LW11DeviceActivationForm
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
device={this.props.device}
|
device={props.device}
|
||||||
onFinish={this.onFinish}
|
onFinish={onFinish}
|
||||||
disabled={this.props.deviceProfile.getSupportsOtaa()}
|
disabled={props.deviceProfile.getSupportsOtaa()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DeviceActivation;
|
export default DeviceActivation;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
@ -27,31 +27,18 @@ interface IProps {
|
|||||||
lastSeenAt?: Date;
|
lastSeenAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DeviceDashboard(props: IProps) {
|
||||||
metricsAggregation: Aggregation;
|
const [metricsAggregation, setMetricsAggregation] = useState<Aggregation>(Aggregation.DAY);
|
||||||
deviceMetrics?: GetDeviceMetricsResponse;
|
const [deviceMetrics, setDeviceMetrics] = useState<GetDeviceMetricsResponse | undefined>(undefined);
|
||||||
deviceLinkMetrics?: GetDeviceLinkMetricsResponse;
|
const [deviceLinkMetrics, setDeviceLinkMetrics] = useState<GetDeviceLinkMetricsResponse | undefined>(undefined);
|
||||||
deviceMetricsLoaded: boolean;
|
const [deviceLinkMetricsLoaded, setDeviceLinkMetricsLoaded] = useState<boolean>(false);
|
||||||
deviceLinkMetricsLoaded: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DeviceDashboard extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
loadMetrics();
|
||||||
super(props);
|
}, [props, metricsAggregation]);
|
||||||
|
|
||||||
this.state = {
|
const loadMetrics = () => {
|
||||||
metricsAggregation: Aggregation.DAY,
|
const agg = metricsAggregation;
|
||||||
deviceMetricsLoaded: false,
|
|
||||||
deviceLinkMetricsLoaded: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.loadMetrics();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMetrics = () => {
|
|
||||||
const agg = this.state.metricsAggregation;
|
|
||||||
const end = moment();
|
const end = moment();
|
||||||
let start = moment();
|
let start = moment();
|
||||||
|
|
||||||
@ -63,19 +50,12 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
start = start.subtract(12, "months");
|
start = start.subtract(12, "months");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(
|
setDeviceLinkMetricsLoaded(false);
|
||||||
{
|
loadLinkMetrics(start.toDate(), end.toDate(), agg);
|
||||||
deviceMetricsLoaded: false,
|
loadDeviceMetrics(start.toDate(), end.toDate(), agg);
|
||||||
deviceLinkMetricsLoaded: false,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.loadLinkMetrics(start.toDate(), end.toDate(), agg);
|
|
||||||
this.loadDeviceMetrics(start.toDate(), end.toDate(), agg);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loadDeviceMetrics = (start: Date, end: Date, agg: Aggregation) => {
|
const loadDeviceMetrics = (start: Date, end: Date, agg: Aggregation) => {
|
||||||
let startPb = new Timestamp();
|
let startPb = new Timestamp();
|
||||||
let endPb = new Timestamp();
|
let endPb = new Timestamp();
|
||||||
|
|
||||||
@ -83,20 +63,17 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
endPb.fromDate(end);
|
endPb.fromDate(end);
|
||||||
|
|
||||||
let req = new GetDeviceMetricsRequest();
|
let req = new GetDeviceMetricsRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
req.setStart(startPb);
|
req.setStart(startPb);
|
||||||
req.setEnd(endPb);
|
req.setEnd(endPb);
|
||||||
req.setAggregation(agg);
|
req.setAggregation(agg);
|
||||||
|
|
||||||
DeviceStore.getMetrics(req, (resp: GetDeviceMetricsResponse) => {
|
DeviceStore.getMetrics(req, (resp: GetDeviceMetricsResponse) => {
|
||||||
this.setState({
|
setDeviceMetrics(resp);
|
||||||
deviceMetrics: resp,
|
|
||||||
deviceMetricsLoaded: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
loadLinkMetrics = (start: Date, end: Date, agg: Aggregation) => {
|
const loadLinkMetrics = (start: Date, end: Date, agg: Aggregation) => {
|
||||||
let startPb = new Timestamp();
|
let startPb = new Timestamp();
|
||||||
let endPb = new Timestamp();
|
let endPb = new Timestamp();
|
||||||
|
|
||||||
@ -104,37 +81,29 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
endPb.fromDate(end);
|
endPb.fromDate(end);
|
||||||
|
|
||||||
let req = new GetDeviceLinkMetricsRequest();
|
let req = new GetDeviceLinkMetricsRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
req.setStart(startPb);
|
req.setStart(startPb);
|
||||||
req.setEnd(endPb);
|
req.setEnd(endPb);
|
||||||
req.setAggregation(agg);
|
req.setAggregation(agg);
|
||||||
|
|
||||||
DeviceStore.getLinkMetrics(req, (resp: GetDeviceLinkMetricsResponse) => {
|
DeviceStore.getLinkMetrics(req, (resp: GetDeviceLinkMetricsResponse) => {
|
||||||
this.setState({
|
setDeviceLinkMetrics(resp);
|
||||||
deviceLinkMetrics: resp,
|
setDeviceLinkMetricsLoaded(true);
|
||||||
deviceLinkMetricsLoaded: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMetricsAggregationChange = (e: RadioChangeEvent) => {
|
const onMetricsAggregationChange = (e: RadioChangeEvent) => {
|
||||||
this.setState(
|
setMetricsAggregation(e.target.value);
|
||||||
{
|
|
||||||
metricsAggregation: e.target.value,
|
|
||||||
},
|
|
||||||
this.loadMetrics,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (deviceLinkMetrics === undefined || deviceMetrics === undefined) {
|
||||||
if (this.state.deviceLinkMetrics === undefined || this.state.deviceMetrics === undefined) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let deviceMetrics = [];
|
let dm = [];
|
||||||
|
|
||||||
{
|
{
|
||||||
let states = this.state.deviceMetrics.getStatesMap();
|
let states = deviceMetrics.getStatesMap();
|
||||||
let keys = states.toArray().map(v => v[0]);
|
let keys = states.toArray().map(v => v[0]);
|
||||||
keys.sort();
|
keys.sort();
|
||||||
|
|
||||||
@ -150,12 +119,12 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
deviceMetrics.push(<Row gutter={24}>{items}</Row>);
|
dm.push(<Row gutter={24}>{items}</Row>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let metrics = this.state.deviceMetrics.getMetricsMap();
|
let metrics = deviceMetrics.getMetricsMap();
|
||||||
let keys = metrics.toArray().map(v => v[0]);
|
let keys = metrics.toArray().map(v => v[0]);
|
||||||
keys.sort();
|
keys.sort();
|
||||||
|
|
||||||
@ -164,26 +133,26 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
let m = metrics.get(k)!;
|
let m = metrics.get(k)!;
|
||||||
return (
|
return (
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<MetricChart metric={m} aggregation={this.state.metricsAggregation} zeroToNull />
|
<MetricChart metric={m} aggregation={metricsAggregation} zeroToNull />
|
||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
deviceMetrics.push(<Row gutter={24}>{items}</Row>);
|
dm.push(<Row gutter={24}>{items}</Row>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastSeenAt = "Never";
|
let lastSeenAt = "Never";
|
||||||
if (this.props.lastSeenAt !== undefined) {
|
if (props.lastSeenAt !== undefined) {
|
||||||
lastSeenAt = moment(this.props.lastSeenAt).format("YYYY-MM-DD HH:mm:ss");
|
lastSeenAt = moment(props.lastSeenAt).format("YYYY-MM-DD HH:mm:ss");
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = !this.state.deviceLinkMetricsLoaded || !this.state.deviceMetrics;
|
const loading = !deviceLinkMetricsLoaded || !deviceMetrics;
|
||||||
|
|
||||||
const aggregations = (
|
const aggregations = (
|
||||||
<Space direction="horizontal">
|
<Space direction="horizontal">
|
||||||
{loading && <Spin size="small" />}
|
{loading && <Spin size="small" />}
|
||||||
<Radio.Group value={this.state.metricsAggregation} onChange={this.onMetricsAggregationChange} size="small">
|
<Radio.Group value={metricsAggregation} onChange={onMetricsAggregationChange} size="small">
|
||||||
<Radio.Button value={Aggregation.HOUR} disabled={loading}>
|
<Radio.Button value={Aggregation.HOUR} disabled={loading}>
|
||||||
24h
|
24h
|
||||||
</Radio.Button>
|
</Radio.Button>
|
||||||
@ -194,7 +163,7 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
1y
|
1y
|
||||||
</Radio.Button>
|
</Radio.Button>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
<Button type="primary" size="small" icon={<ReloadOutlined />} onClick={this.loadMetrics} disabled={loading} />
|
<Button type="primary" size="small" icon={<ReloadOutlined />} onClick={loadMetrics} disabled={loading} />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -205,13 +174,13 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
<Descriptions.Item label="Last seen">{lastSeenAt}</Descriptions.Item>
|
<Descriptions.Item label="Last seen">{lastSeenAt}</Descriptions.Item>
|
||||||
<Descriptions.Item label="Device profile">
|
<Descriptions.Item label="Device profile">
|
||||||
<Link
|
<Link
|
||||||
to={`/tenants/${this.props.deviceProfile.getTenantId()}/device-profiles/${this.props.deviceProfile.getId()}/edit`}
|
to={`/tenants/${props.deviceProfile.getTenantId()}/device-profiles/${props.deviceProfile.getId()}/edit`}
|
||||||
>
|
>
|
||||||
{this.props.deviceProfile.getName()}
|
{props.deviceProfile.getName()}
|
||||||
</Link>
|
</Link>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="Enabled">{this.props.device.getIsDisabled() ? "no" : "yes"}</Descriptions.Item>
|
<Descriptions.Item label="Enabled">{props.device.getIsDisabled() ? "no" : "yes"}</Descriptions.Item>
|
||||||
<Descriptions.Item label="Description">{this.props.device.getDescription()}</Descriptions.Item>
|
<Descriptions.Item label="Description">{props.device.getDescription()}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</Card>
|
</Card>
|
||||||
<Tabs tabBarExtraContent={aggregations}>
|
<Tabs tabBarExtraContent={aggregations}>
|
||||||
@ -219,61 +188,46 @@ class DeviceDashboard extends Component<IProps, IState> {
|
|||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<MetricChart
|
<MetricChart metric={deviceLinkMetrics.getRxPackets()!} aggregation={metricsAggregation} />
|
||||||
metric={this.state.deviceLinkMetrics.getRxPackets()!}
|
|
||||||
aggregation={this.state.metricsAggregation}
|
|
||||||
/>
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<MetricChart
|
<MetricChart metric={deviceLinkMetrics.getGwRssi()!} aggregation={metricsAggregation} zeroToNull />
|
||||||
metric={this.state.deviceLinkMetrics.getGwRssi()!}
|
|
||||||
aggregation={this.state.metricsAggregation}
|
|
||||||
zeroToNull
|
|
||||||
/>
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<MetricChart
|
<MetricChart metric={deviceLinkMetrics.getGwSnr()!} aggregation={metricsAggregation} zeroToNull />
|
||||||
metric={this.state.deviceLinkMetrics.getGwSnr()!}
|
|
||||||
aggregation={this.state.metricsAggregation}
|
|
||||||
zeroToNull
|
|
||||||
/>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<MetricHeatmap
|
<MetricHeatmap
|
||||||
metric={this.state.deviceLinkMetrics.getRxPacketsPerFreq()!}
|
metric={deviceLinkMetrics.getRxPacketsPerFreq()!}
|
||||||
aggregation={this.state.metricsAggregation}
|
aggregation={metricsAggregation}
|
||||||
fromColor="rgb(227, 242, 253)"
|
fromColor="rgb(227, 242, 253)"
|
||||||
toColor="rgb(33, 150, 243, 1)"
|
toColor="rgb(33, 150, 243, 1)"
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<MetricHeatmap
|
<MetricHeatmap
|
||||||
metric={this.state.deviceLinkMetrics.getRxPacketsPerDr()!}
|
metric={deviceLinkMetrics.getRxPacketsPerDr()!}
|
||||||
aggregation={this.state.metricsAggregation}
|
aggregation={metricsAggregation}
|
||||||
fromColor="rgb(227, 242, 253)"
|
fromColor="rgb(227, 242, 253)"
|
||||||
toColor="rgb(33, 150, 243, 1)"
|
toColor="rgb(33, 150, 243, 1)"
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<MetricBar
|
<MetricBar metric={deviceLinkMetrics.getErrors()!} aggregation={metricsAggregation} />
|
||||||
metric={this.state.deviceLinkMetrics.getErrors()!}
|
|
||||||
aggregation={this.state.metricsAggregation}
|
|
||||||
/>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Space>
|
</Space>
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="Device metrics" key="2">
|
<Tabs.TabPane tab="Device metrics" key="2">
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
{deviceMetrics}
|
{dm}
|
||||||
</Space>
|
</Space>
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DeviceDashboard;
|
export default DeviceDashboard;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
import { Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||||
import { StreamDeviceEventsRequest, LogItem } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
import { StreamDeviceEventsRequest, LogItem } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||||
@ -10,55 +10,31 @@ interface IProps {
|
|||||||
device: Device;
|
device: Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DeviceEvents(props: IProps) {
|
||||||
events: LogItem[];
|
const [events, setEvents] = useState<LogItem[]>([]);
|
||||||
cancelFunc?: () => void;
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onMessage = (l: LogItem) => {
|
||||||
|
setEvents(e => {
|
||||||
|
if (e.length === 0 || parseInt(l.getId().replace("-", "")) > parseInt(e[0].getId().replace("-", ""))) {
|
||||||
|
e.unshift(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeviceEvents extends Component<IProps, IState> {
|
return e;
|
||||||
constructor(props: IProps) {
|
});
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
events: [],
|
|
||||||
cancelFunc: undefined,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.connectStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.state.cancelFunc !== undefined) {
|
|
||||||
this.state.cancelFunc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectStream = () => {
|
|
||||||
let req = new StreamDeviceEventsRequest();
|
let req = new StreamDeviceEventsRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
|
|
||||||
let cancelFunc = InternalStore.streamDeviceEvents(req, this.onMessage);
|
let cancelFunc = InternalStore.streamDeviceEvents(req, onMessage);
|
||||||
this.setState({
|
|
||||||
cancelFunc: cancelFunc,
|
return () => {
|
||||||
});
|
cancelFunc();
|
||||||
};
|
};
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
onMessage = (l: LogItem) => {
|
return <LogTable logs={events} />;
|
||||||
let events = this.state.events;
|
|
||||||
|
|
||||||
if (events.length === 0 || parseInt(l.getId().replace("-", "")) > parseInt(events[0].getId().replace("-", ""))) {
|
|
||||||
events.unshift(l);
|
|
||||||
this.setState({
|
|
||||||
events: events,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <LogTable logs={this.state.events} />;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DeviceEvents;
|
export default DeviceEvents;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Form, Input, Row, Col, Button, Tabs, Switch } from "antd";
|
import { Form, Input, Row, Col, Button, Tabs, Switch } from "antd";
|
||||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
@ -24,11 +22,11 @@ interface IProps {
|
|||||||
update?: boolean;
|
update?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeviceForm extends Component<IProps> {
|
function DeviceForm(props: IProps) {
|
||||||
formRef = React.createRef<any>();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
onFinish = (values: Device.AsObject) => {
|
const onFinish = (values: Device.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let d = new Device();
|
let d = new Device();
|
||||||
|
|
||||||
d.setApplicationId(v.applicationId);
|
d.setApplicationId(v.applicationId);
|
||||||
@ -50,12 +48,12 @@ class DeviceForm extends Component<IProps> {
|
|||||||
d.getVariablesMap().set(elm[0], elm[1]);
|
d.getVariablesMap().set(elm[0], elm[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onFinish(d);
|
props.onFinish(d);
|
||||||
};
|
};
|
||||||
|
|
||||||
getDeviceProfileOptions = (search: string, fn: OptionsCallbackFunc) => {
|
const getDeviceProfileOptions = (search: string, fn: OptionsCallbackFunc) => {
|
||||||
let req = new ListDeviceProfilesRequest();
|
let req = new ListDeviceProfilesRequest();
|
||||||
req.setTenantId(this.props.tenant.getId());
|
req.setTenantId(props.tenant.getId());
|
||||||
req.setSearch(search);
|
req.setSearch(search);
|
||||||
req.setLimit(10);
|
req.setLimit(10);
|
||||||
|
|
||||||
@ -63,11 +61,12 @@ class DeviceForm extends Component<IProps> {
|
|||||||
const options = resp.getResultList().map((o, i) => {
|
const options = resp.getResultList().map((o, i) => {
|
||||||
return { label: o.getName(), value: o.getId() };
|
return { label: o.getName(), value: o.getId() };
|
||||||
});
|
});
|
||||||
|
|
||||||
fn(options);
|
fn(options);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getDeviceProfileOption = (id: string, fn: OptionCallbackFunc) => {
|
const getDeviceProfileOption = (id: string, fn: OptionCallbackFunc) => {
|
||||||
let req = new GetDeviceProfileRequest();
|
let req = new GetDeviceProfileRequest();
|
||||||
req.setId(id);
|
req.setId(id);
|
||||||
|
|
||||||
@ -79,14 +78,8 @@ class DeviceForm extends Component<IProps> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish} form={form}>
|
||||||
layout="vertical"
|
|
||||||
initialValues={this.props.initialValues.toObject()}
|
|
||||||
onFinish={this.onFinish}
|
|
||||||
ref={this.formRef}
|
|
||||||
>
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tabs.TabPane tab="Device" key="1">
|
<Tabs.TabPane tab="Device" key="1">
|
||||||
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
||||||
@ -100,9 +93,8 @@ class DeviceForm extends Component<IProps> {
|
|||||||
<EuiInput
|
<EuiInput
|
||||||
label="Device EUI (EUI64)"
|
label="Device EUI (EUI64)"
|
||||||
name="devEui"
|
name="devEui"
|
||||||
value={this.props.initialValues.getDevEui()}
|
value={props.initialValues.getDevEui()}
|
||||||
formRef={this.formRef}
|
disabled={props.update}
|
||||||
disabled={this.props.update}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
@ -110,8 +102,7 @@ class DeviceForm extends Component<IProps> {
|
|||||||
<EuiInput
|
<EuiInput
|
||||||
label="Join EUI (EUI64)"
|
label="Join EUI (EUI64)"
|
||||||
name="joinEui"
|
name="joinEui"
|
||||||
value={this.props.initialValues.getJoinEui()}
|
value={props.initialValues.getJoinEui()}
|
||||||
formRef={this.formRef}
|
|
||||||
tooltip="The Join EUI will be automatically set / updated on OTAA. However, in some cases this field must be configured before OTAA (e.g. OTAA using a Relay)."
|
tooltip="The Join EUI will be automatically set / updated on OTAA. However, in some cases this field must be configured before OTAA (e.g. OTAA using a Relay)."
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
@ -119,9 +110,8 @@ class DeviceForm extends Component<IProps> {
|
|||||||
<AutocompleteInput
|
<AutocompleteInput
|
||||||
label="Device profile"
|
label="Device profile"
|
||||||
name="deviceProfileId"
|
name="deviceProfileId"
|
||||||
formRef={this.formRef}
|
getOption={getDeviceProfileOption}
|
||||||
getOption={this.getDeviceProfileOption}
|
getOptions={getDeviceProfileOptions}
|
||||||
getOptions={this.getDeviceProfileOptions}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
@ -236,6 +226,5 @@ class DeviceForm extends Component<IProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DeviceForm;
|
export default DeviceForm;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
import { Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||||
import { StreamDeviceFramesRequest, LogItem } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
import { StreamDeviceFramesRequest, LogItem } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||||
@ -10,55 +10,31 @@ interface IProps {
|
|||||||
device: Device;
|
device: Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DeviceFrames(props: IProps) {
|
||||||
frames: LogItem[];
|
const [frames, setFrames] = useState<LogItem[]>([]);
|
||||||
cancelFunc?: () => void;
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onMessage = (l: LogItem) => {
|
||||||
|
setFrames(f => {
|
||||||
|
if (f.length === 0 || parseInt(l.getId().replace("-", "")) > parseInt(f[0].getId().replace("-", ""))) {
|
||||||
|
f.unshift(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeviceFrames extends Component<IProps, IState> {
|
return f;
|
||||||
constructor(props: IProps) {
|
});
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
frames: [],
|
|
||||||
cancelFunc: undefined,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.connectStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.state.cancelFunc !== undefined) {
|
|
||||||
this.state.cancelFunc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectStream = () => {
|
|
||||||
let req = new StreamDeviceFramesRequest();
|
let req = new StreamDeviceFramesRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
|
|
||||||
let cancelFunc = InternalStore.streamDeviceFrames(req, this.onMessage);
|
let cancelFunc = InternalStore.streamDeviceFrames(req, onMessage);
|
||||||
this.setState({
|
|
||||||
cancelFunc: cancelFunc,
|
return () => {
|
||||||
});
|
cancelFunc();
|
||||||
};
|
};
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
onMessage = (l: LogItem) => {
|
return <LogTable logs={frames} />;
|
||||||
let frames = this.state.frames;
|
|
||||||
|
|
||||||
if (frames.length === 0 || parseInt(l.getId().replace("-", "")) > parseInt(frames[0].getId().replace("-", ""))) {
|
|
||||||
frames.unshift(l);
|
|
||||||
this.setState({
|
|
||||||
frames: frames,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <LogTable logs={this.state.frames} />;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DeviceFrames;
|
export default DeviceFrames;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Route, Switch, RouteComponentProps, Link } from "react-router-dom";
|
import { Route, Routes, useParams, Link, useNavigate, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
import { Space, Breadcrumb, Card, Button, PageHeader, Menu } from "antd";
|
import { Space, Breadcrumb, Card, Button, Menu } from "antd";
|
||||||
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -30,82 +31,57 @@ import DeviceEvents from "./DeviceEvents";
|
|||||||
import DeviceQueue from "./DeviceQueue";
|
import DeviceQueue from "./DeviceQueue";
|
||||||
import DeviceActivation from "./DeviceActivation";
|
import DeviceActivation from "./DeviceActivation";
|
||||||
|
|
||||||
interface MatchParams {
|
interface IProps {
|
||||||
devEui: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps<MatchParams> {
|
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DeviceLayout(props: IProps) {
|
||||||
device?: Device;
|
const { devEui } = useParams();
|
||||||
deviceProfile?: DeviceProfile;
|
const navigate = useNavigate();
|
||||||
lastSeenAt?: Date;
|
const location = useLocation();
|
||||||
}
|
|
||||||
|
|
||||||
class DeviceLayout extends Component<IProps, IState> {
|
const [device, setDevice] = useState<Device | undefined>(undefined);
|
||||||
constructor(props: IProps) {
|
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
|
||||||
super(props);
|
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
useEffect(() => {
|
||||||
this.getDevice(this.getDeviceProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDevice = (cb: () => void) => {
|
|
||||||
let req = new GetDeviceRequest();
|
let req = new GetDeviceRequest();
|
||||||
req.setDevEui(this.props.match.params.devEui);
|
req.setDevEui(devEui!);
|
||||||
|
|
||||||
DeviceStore.get(req, (resp: GetDeviceResponse) => {
|
DeviceStore.get(req, (resp: GetDeviceResponse) => {
|
||||||
this.setState(
|
setDevice(resp.getDevice());
|
||||||
{
|
|
||||||
device: resp.getDevice(),
|
|
||||||
},
|
|
||||||
cb,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (resp.getLastSeenAt() !== undefined) {
|
if (resp.getLastSeenAt() !== undefined) {
|
||||||
this.setState({
|
setLastSeenAt(resp.getLastSeenAt()!.toDate());
|
||||||
lastSeenAt: resp.getLastSeenAt()!.toDate(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
getDeviceProfile = () => {
|
|
||||||
let req = new GetDeviceProfileRequest();
|
let req = new GetDeviceProfileRequest();
|
||||||
req.setId(this.state.device!.getDeviceProfileId());
|
req.setId(resp.getDevice()!.getDeviceProfileId());
|
||||||
|
|
||||||
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
||||||
this.setState({
|
setDeviceProfile(resp.getDeviceProfile());
|
||||||
deviceProfile: resp.getDeviceProfile(),
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}, [devEui]);
|
||||||
|
|
||||||
deleteDevice = () => {
|
const deleteDevice = () => {
|
||||||
let req = new DeleteDeviceRequest();
|
let req = new DeleteDeviceRequest();
|
||||||
req.setDevEui(this.props.match.params.devEui);
|
req.setDevEui(devEui!);
|
||||||
|
|
||||||
DeviceStore.delete(req, () => {
|
DeviceStore.delete(req, () => {
|
||||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`);
|
navigate(`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const dp = deviceProfile;
|
||||||
const device = this.state.device;
|
|
||||||
const dp = this.state.deviceProfile;
|
|
||||||
if (!device || !dp) {
|
if (!device || !dp) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenant = this.props.tenant;
|
const tenant = props.tenant;
|
||||||
const app = this.props.application;
|
const app = props.application;
|
||||||
|
|
||||||
const path = this.props.history.location.pathname;
|
const path = location.pathname;
|
||||||
let tab = "dashboard";
|
let tab = "dashboard";
|
||||||
|
|
||||||
if (path.endsWith("edit")) {
|
if (path.endsWith("edit")) {
|
||||||
@ -137,26 +113,24 @@ class DeviceLayout extends Component<IProps, IState> {
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
<Link to={`/tenants/${props.tenant.getId()}`}>{props.tenant.getName()}</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
<Link to={`/tenants/${props.tenant.getId()}/applications`}>Applications</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>
|
<Link to={`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}`}>
|
||||||
{this.props.application.getName()}
|
{props.application.getName()}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>
|
<Link to={`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}`}>Devices</Link>
|
||||||
Devices
|
|
||||||
</Link>
|
|
||||||
</span>
|
</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
@ -167,8 +141,8 @@ class DeviceLayout extends Component<IProps, IState> {
|
|||||||
title={device.getName()}
|
title={device.getName()}
|
||||||
subTitle={`device eui: ${device.getDevEui()}`}
|
subTitle={`device eui: ${device.getDevEui()}`}
|
||||||
extra={[
|
extra={[
|
||||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
<Admin tenantId={props.tenant.getId()} isDeviceAdmin>
|
||||||
<DeleteConfirm typ="device" confirm={device.getName()} onConfirm={this.deleteDevice}>
|
<DeleteConfirm typ="device" confirm={device.getName()} onConfirm={deleteDevice}>
|
||||||
<Button danger type="primary">
|
<Button danger type="primary">
|
||||||
Delete device
|
Delete device
|
||||||
</Button>
|
</Button>
|
||||||
@ -216,53 +190,24 @@ class DeviceLayout extends Component<IProps, IState> {
|
|||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Switch>
|
<Routes>
|
||||||
|
<Route path="/" element={<DeviceDashboard device={device} lastSeenAt={lastSeenAt} deviceProfile={dp} />} />
|
||||||
|
<Route path="/edit" element={<EditDevice device={device} application={app} tenant={tenant} />} />
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/keys"
|
||||||
path={this.props.match.path}
|
element={<SetDeviceKeys device={device} application={app} tenant={tenant} deviceProfile={dp} />}
|
||||||
render={props => (
|
|
||||||
<DeviceDashboard device={device} lastSeenAt={this.state.lastSeenAt} deviceProfile={dp} {...props} />
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
<Route path="/frames" element={<DeviceFrames device={device} />} />
|
||||||
|
<Route path="/events" element={<DeviceEvents device={device} />} />
|
||||||
|
<Route path="/queue" element={<DeviceQueue device={device} />} />
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/activation"
|
||||||
path={`${this.props.match.path}/edit`}
|
element={<DeviceActivation device={device} deviceProfile={dp} tenant={tenant} application={app} />}
|
||||||
render={props => <EditDevice device={device} application={app} tenant={tenant} {...props} />}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
</Routes>
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/keys`}
|
|
||||||
render={props => (
|
|
||||||
<SetDeviceKeys device={device} application={app} tenant={tenant} deviceProfile={dp} {...props} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/frames`}
|
|
||||||
render={props => <DeviceFrames device={device} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/events`}
|
|
||||||
render={props => <DeviceEvents device={device} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/queue`}
|
|
||||||
render={props => <DeviceQueue device={device} {...props} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path={`${this.props.match.path}/activation`}
|
|
||||||
render={props => (
|
|
||||||
<DeviceActivation device={device} deviceProfile={dp} tenant={tenant} application={app} {...props} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Switch>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DeviceLayout;
|
export default DeviceLayout;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { Struct } from "google-protobuf/google/protobuf/struct_pb";
|
import { Struct } from "google-protobuf/google/protobuf/struct_pb";
|
||||||
|
|
||||||
@ -25,22 +25,11 @@ interface IProps {
|
|||||||
device: Device;
|
device: Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function DeviceQueue(props: IProps) {
|
||||||
refreshCounter: number;
|
const [refreshCounter, setRefreshCounter] = useState<number>(0);
|
||||||
}
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
class DeviceQueue extends Component<IProps, IState> {
|
const columns: ColumnsType<DeviceQueueItem.AsObject> = [
|
||||||
formRef = React.createRef<any>();
|
|
||||||
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
refreshCounter: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
columns = (): ColumnsType<DeviceQueueItem.AsObject> => {
|
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
title: "ID",
|
title: "ID",
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
@ -101,11 +90,10 @@ class DeviceQueue extends Component<IProps, IState> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
const getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||||
let req = new GetDeviceQueueItemsRequest();
|
let req = new GetDeviceQueueItemsRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
|
|
||||||
DeviceStore.getQueue(req, (resp: GetDeviceQueueItemsResponse) => {
|
DeviceStore.getQueue(req, (resp: GetDeviceQueueItemsResponse) => {
|
||||||
const obj = resp.toObject();
|
const obj = resp.toObject();
|
||||||
@ -113,25 +101,23 @@ class DeviceQueue extends Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
refreshQueue = () => {
|
const refreshQueue = () => {
|
||||||
this.setState({
|
setRefreshCounter(refreshCounter + 1);
|
||||||
refreshCounter: this.state.refreshCounter + 1,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
flushQueue = () => {
|
const flushQueue = () => {
|
||||||
let req = new FlushDeviceQueueRequest();
|
let req = new FlushDeviceQueueRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
DeviceStore.flushQueue(req, () => {
|
DeviceStore.flushQueue(req, () => {
|
||||||
this.refreshQueue();
|
refreshQueue();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onEnqueue = (values: any) => {
|
const onEnqueue = (values: any) => {
|
||||||
let req = new EnqueueDeviceQueueItemRequest();
|
let req = new EnqueueDeviceQueueItemRequest();
|
||||||
let item = new DeviceQueueItem();
|
let item = new DeviceQueueItem();
|
||||||
|
|
||||||
item.setDevEui(this.props.device.getDevEui());
|
item.setDevEui(props.device.getDevEui());
|
||||||
item.setFPort(values.fPort);
|
item.setFPort(values.fPort);
|
||||||
item.setConfirmed(values.confirmed);
|
item.setConfirmed(values.confirmed);
|
||||||
|
|
||||||
@ -163,16 +149,15 @@ class DeviceQueue extends Component<IProps, IState> {
|
|||||||
req.setQueueItem(item);
|
req.setQueueItem(item);
|
||||||
|
|
||||||
DeviceStore.enqueue(req, _ => {
|
DeviceStore.enqueue(req, _ => {
|
||||||
this.formRef.current.resetFields();
|
form.resetFields();
|
||||||
this.refreshQueue();
|
refreshQueue();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
<Card title="Enqueue">
|
<Card title="Enqueue">
|
||||||
<Form layout="horizontal" onFinish={this.onEnqueue} ref={this.formRef} initialValues={{ fPort: 1 }}>
|
<Form layout="horizontal" onFinish={onEnqueue} form={form} initialValues={{ fPort: 1 }}>
|
||||||
<Row>
|
<Row>
|
||||||
<Space direction="horizontal" style={{ width: "100%" }} size="large">
|
<Space direction="horizontal" style={{ width: "100%" }} size="large">
|
||||||
<Form.Item name="confirmed" label="Confirmed" valuePropName="checked">
|
<Form.Item name="confirmed" label="Confirmed" valuePropName="checked">
|
||||||
@ -195,7 +180,7 @@ class DeviceQueue extends Component<IProps, IState> {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="JSON" key="3">
|
<Tabs.TabPane tab="JSON" key="3">
|
||||||
<CodeEditor name="json" value="{}" formRef={this.formRef} />
|
<CodeEditor name="json" />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<Button type="primary" htmlType="submit">
|
<Button type="primary" htmlType="submit">
|
||||||
@ -205,24 +190,17 @@ class DeviceQueue extends Component<IProps, IState> {
|
|||||||
</Card>
|
</Card>
|
||||||
<Row justify="end">
|
<Row justify="end">
|
||||||
<Space direction="horizontal" size="large">
|
<Space direction="horizontal" size="large">
|
||||||
<Button icon={<RedoOutlined />} onClick={this.refreshQueue}>
|
<Button icon={<RedoOutlined />} onClick={refreshQueue}>
|
||||||
Reload
|
Reload
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm title="Are you sure you want to flush the queue?" placement="left" onConfirm={this.flushQueue}>
|
<Popconfirm title="Are you sure you want to flush the queue?" placement="left" onConfirm={flushQueue}>
|
||||||
<Button icon={<DeleteOutlined />}>Flush queue</Button>
|
<Button icon={<DeleteOutlined />}>Flush queue</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
</Row>
|
</Row>
|
||||||
<DataTable
|
<DataTable columns={columns} getPage={getPage} refreshKey={refreshCounter} rowKey="id" noPagination />
|
||||||
columns={this.columns()}
|
|
||||||
getPage={this.getPage}
|
|
||||||
refreshKey={this.state.refreshCounter}
|
|
||||||
rowKey="id"
|
|
||||||
noPagination
|
|
||||||
/>
|
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default DeviceQueue;
|
export default DeviceQueue;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
|
||||||
|
|
||||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||||
@ -8,27 +7,25 @@ import { Device, UpdateDeviceRequest } from "@chirpstack/chirpstack-api-grpc-web
|
|||||||
import DeviceStore from "../../stores/DeviceStore";
|
import DeviceStore from "../../stores/DeviceStore";
|
||||||
import DeviceForm from "./DeviceForm";
|
import DeviceForm from "./DeviceForm";
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
application: Application;
|
application: Application;
|
||||||
device: Device;
|
device: Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditDevice extends Component<IProps> {
|
function EditDevice(props: IProps) {
|
||||||
onFinish = (obj: Device) => {
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const onFinish = (obj: Device) => {
|
||||||
let req = new UpdateDeviceRequest();
|
let req = new UpdateDeviceRequest();
|
||||||
req.setDevice(obj);
|
req.setDevice(obj);
|
||||||
|
|
||||||
DeviceStore.update(req, () => {
|
DeviceStore.update(req, () => {
|
||||||
this.props.history.push(
|
navigate(`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}/devices/${obj.getDevEui()}`);
|
||||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
return <DeviceForm initialValues={props.device} onFinish={onFinish} tenant={props.tenant} update />;
|
||||||
return <DeviceForm initialValues={this.props.device} onFinish={this.onFinish} tenant={this.props.tenant} update />;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditDevice;
|
export default EditDevice;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
@ -42,54 +42,34 @@ interface IProps {
|
|||||||
application: Application;
|
application: Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function ListDevices(props: IProps) {
|
||||||
selectedRowIds: string[];
|
const [selectedRowIds, setSelectedRowIds] = useState<string[]>([]);
|
||||||
multicastGroups: MulticastGroupListItem[];
|
const [multicastGroups, setMulticastGroups] = useState<MulticastGroupListItem[]>([]);
|
||||||
relays: RelayListItem[];
|
const [relays, setRelays] = useState<RelayListItem[]>([]);
|
||||||
mgModalVisible: boolean;
|
const [mgModalVisible, setMgModalVisible] = useState<boolean>(false);
|
||||||
relayModalVisible: boolean;
|
const [relayModalVisible, setRelayModalVisible] = useState<boolean>(false);
|
||||||
mgSelected: string;
|
const [mgSelected, setMgSelected] = useState<string>("");
|
||||||
relaySelected: string;
|
const [relaySelected, setRelaySelected] = useState<string>("");
|
||||||
}
|
|
||||||
|
|
||||||
class ListDevices extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
selectedRowIds: [],
|
|
||||||
multicastGroups: [],
|
|
||||||
relays: [],
|
|
||||||
mgModalVisible: false,
|
|
||||||
relayModalVisible: false,
|
|
||||||
mgSelected: "",
|
|
||||||
relaySelected: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let mgReq = new ListMulticastGroupsRequest();
|
let mgReq = new ListMulticastGroupsRequest();
|
||||||
mgReq.setLimit(999);
|
mgReq.setLimit(999);
|
||||||
mgReq.setApplicationId(this.props.application.getId());
|
mgReq.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
MulticastGroupStore.list(mgReq, (resp: ListMulticastGroupsResponse) => {
|
MulticastGroupStore.list(mgReq, (resp: ListMulticastGroupsResponse) => {
|
||||||
this.setState({
|
setMulticastGroups(resp.getResultList());
|
||||||
multicastGroups: resp.getResultList(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let relayReq = new ListRelaysRequest();
|
let relayReq = new ListRelaysRequest();
|
||||||
relayReq.setLimit(999);
|
relayReq.setLimit(999);
|
||||||
relayReq.setApplicationId(this.props.application.getId());
|
relayReq.setApplicationId(props.application.getId());
|
||||||
|
|
||||||
RelayStore.list(relayReq, (resp: ListRelaysResponse) => {
|
RelayStore.list(relayReq, (resp: ListRelaysResponse) => {
|
||||||
this.setState({
|
setRelays(resp.getResultList());
|
||||||
relays: resp.getResultList(),
|
|
||||||
});
|
});
|
||||||
});
|
}, [props]);
|
||||||
}
|
|
||||||
|
|
||||||
columns = (): ColumnsType<DeviceListItem.AsObject> => {
|
const columns: ColumnsType<DeviceListItem.AsObject> = [
|
||||||
return [
|
|
||||||
{
|
{
|
||||||
title: "Last seen",
|
title: "Last seen",
|
||||||
dataIndex: "lastSeenAt",
|
dataIndex: "lastSeenAt",
|
||||||
@ -111,7 +91,7 @@ class ListDevices extends Component<IProps, IState> {
|
|||||||
width: 250,
|
width: 250,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link
|
<Link
|
||||||
to={`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/devices/${
|
to={`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/devices/${
|
||||||
record.devEui
|
record.devEui
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -129,7 +109,7 @@ class ListDevices extends Component<IProps, IState> {
|
|||||||
dataIndex: "deviceProfileName",
|
dataIndex: "deviceProfileName",
|
||||||
key: "deviceProfileName",
|
key: "deviceProfileName",
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={`/tenants/${this.props.application.getTenantId()}/device-profiles/${record.deviceProfileId}/edit`}>
|
<Link to={`/tenants/${props.application.getTenantId()}/device-profiles/${record.deviceProfileId}/edit`}>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
@ -157,11 +137,10 @@ class ListDevices extends Component<IProps, IState> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
const getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||||
let req = new ListDevicesRequest();
|
let req = new ListDevicesRequest();
|
||||||
req.setApplicationId(this.props.application.getId());
|
req.setApplicationId(props.application.getId());
|
||||||
req.setLimit(limit);
|
req.setLimit(limit);
|
||||||
req.setOffset(offset);
|
req.setOffset(offset);
|
||||||
|
|
||||||
@ -171,148 +150,114 @@ class ListDevices extends Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onRowsSelectChange = (ids: string[]) => {
|
const onRowsSelectChange = (ids: string[]) => {
|
||||||
this.setState({
|
setSelectedRowIds(ids);
|
||||||
selectedRowIds: ids,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
showMgModal = () => {
|
const showMgModal = () => {
|
||||||
this.setState({
|
setMgModalVisible(true);
|
||||||
mgModalVisible: true,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
showRelayModal = () => {
|
const showRelayModal = () => {
|
||||||
this.setState({
|
setRelayModalVisible(true);
|
||||||
relayModalVisible: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
hideMgModal = () => {
|
|
||||||
this.setState({
|
|
||||||
mgModalVisible: false,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
hideRelayModal = () => {
|
const hideMgModal = () => {
|
||||||
this.setState({
|
setMgModalVisible(false);
|
||||||
relayModalVisible: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMgSelected = (value: string) => {
|
|
||||||
this.setState({
|
|
||||||
mgSelected: value,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onRelaySelected = (value: string) => {
|
const hideRelayModal = () => {
|
||||||
this.setState({
|
setRelayModalVisible(false);
|
||||||
relaySelected: value,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMgModalOk = () => {
|
const onMgSelected = (value: string) => {
|
||||||
for (let devEui of this.state.selectedRowIds) {
|
setMgSelected(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRelaySelected = (value: string) => {
|
||||||
|
setRelaySelected(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMgModalOk = () => {
|
||||||
|
for (let devEui of selectedRowIds) {
|
||||||
let req = new AddDeviceToMulticastGroupRequest();
|
let req = new AddDeviceToMulticastGroupRequest();
|
||||||
req.setMulticastGroupId(this.state.mgSelected);
|
req.setMulticastGroupId(mgSelected);
|
||||||
req.setDevEui(devEui);
|
req.setDevEui(devEui);
|
||||||
|
|
||||||
MulticastGroupStore.addDevice(req, () => {});
|
MulticastGroupStore.addDevice(req, () => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setMgModalVisible(false);
|
||||||
mgModalVisible: false,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleRelayModalOk = () => {
|
const handleRelayModalOk = () => {
|
||||||
for (let devEui of this.state.selectedRowIds) {
|
for (let devEui of selectedRowIds) {
|
||||||
let req = new AddRelayDeviceRequest();
|
let req = new AddRelayDeviceRequest();
|
||||||
req.setRelayDevEui(this.state.relaySelected);
|
req.setRelayDevEui(relaySelected);
|
||||||
req.setDeviceDevEui(devEui);
|
req.setDeviceDevEui(devEui);
|
||||||
|
|
||||||
RelayStore.addDevice(req, () => {});
|
RelayStore.addDevice(req, () => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setRelayModalVisible(false);
|
||||||
relayModalVisible: false,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item onClick={this.showMgModal}>Add to multicast-group</Menu.Item>
|
<Menu.Item onClick={showMgModal}>Add to multicast-group</Menu.Item>
|
||||||
<Menu.Item onClick={this.showRelayModal}>Add to relay</Menu.Item>
|
<Menu.Item onClick={showRelayModal}>Add to relay</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
const mgOptions = this.state.multicastGroups.map((mg, i) => (
|
const mgOptions = multicastGroups.map((mg, i) => <Select.Option value={mg.getId()}>{mg.getName()}</Select.Option>);
|
||||||
<Select.Option value={mg.getId()}>{mg.getName()}</Select.Option>
|
|
||||||
));
|
|
||||||
|
|
||||||
const relayOptions = this.state.relays.map((r, i) => (
|
const relayOptions = relays.map((r, i) => <Select.Option value={r.getDevEui()}>{r.getName()}</Select.Option>);
|
||||||
<Select.Option value={r.getDevEui()}>{r.getName()}</Select.Option>
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||||
<Modal
|
<Modal
|
||||||
title="Add selected devices to multicast-group"
|
title="Add selected devices to multicast-group"
|
||||||
visible={this.state.mgModalVisible}
|
visible={mgModalVisible}
|
||||||
onOk={this.handleMgModalOk}
|
onOk={handleMgModalOk}
|
||||||
onCancel={this.hideMgModal}
|
onCancel={hideMgModal}
|
||||||
okButtonProps={{ disabled: this.state.mgSelected === "" }}
|
okButtonProps={{ disabled: mgSelected === "" }}
|
||||||
>
|
>
|
||||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||||
<Select style={{ width: "100%" }} onChange={this.onMgSelected} placeholder="Select Multicast-group">
|
<Select style={{ width: "100%" }} onChange={onMgSelected} placeholder="Select Multicast-group">
|
||||||
{mgOptions}
|
{mgOptions}
|
||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal
|
<Modal
|
||||||
title="Add selected devices to relay"
|
title="Add selected devices to relay"
|
||||||
visible={this.state.relayModalVisible}
|
visible={relayModalVisible}
|
||||||
onOk={this.handleRelayModalOk}
|
onOk={handleRelayModalOk}
|
||||||
onCancel={this.hideRelayModal}
|
onCancel={hideRelayModal}
|
||||||
okButtonProps={{ disabled: this.state.relaySelected === "" }}
|
okButtonProps={{ disabled: relaySelected === "" }}
|
||||||
>
|
>
|
||||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||||
<Select style={{ width: "100%" }} onChange={this.onRelaySelected} placeholder="Select Relay">
|
<Select style={{ width: "100%" }} onChange={onRelaySelected} placeholder="Select Relay">
|
||||||
{relayOptions}
|
{relayOptions}
|
||||||
</Select>
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Admin tenantId={this.props.application.getTenantId()} isDeviceAdmin>
|
<Admin tenantId={props.application.getTenantId()} isDeviceAdmin>
|
||||||
<Space direction="horizontal" style={{ float: "right" }}>
|
<Space direction="horizontal" style={{ float: "right" }}>
|
||||||
<Button type="primary">
|
<Button type="primary">
|
||||||
<Link
|
<Link
|
||||||
to={`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/devices/create`}
|
to={`/tenants/${props.application.getTenantId()}/applications/${props.application.getId()}/devices/create`}
|
||||||
>
|
>
|
||||||
Add device
|
Add device
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Dropdown
|
<Dropdown placement="bottomRight" overlay={menu} trigger={["click"]} disabled={selectedRowIds.length === 0}>
|
||||||
placement="bottomRight"
|
|
||||||
overlay={menu}
|
|
||||||
trigger={["click"]}
|
|
||||||
disabled={this.state.selectedRowIds.length === 0}
|
|
||||||
>
|
|
||||||
<Button>Selected devices</Button>
|
<Button>Selected devices</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Space>
|
</Space>
|
||||||
</Admin>
|
</Admin>
|
||||||
<DataTable
|
<DataTable columns={columns} getPage={getPage} onRowsSelectChange={onRowsSelectChange} rowKey="devEui" />
|
||||||
columns={this.columns()}
|
|
||||||
getPage={this.getPage}
|
|
||||||
onRowsSelectChange={this.onRowsSelectChange}
|
|
||||||
rowKey="devEui"
|
|
||||||
/>
|
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default ListDevices;
|
export default ListDevices;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Form, Button, Space, Popconfirm } from "antd";
|
import { Form, Button, Space, Popconfirm } from "antd";
|
||||||
|
|
||||||
@ -25,11 +25,11 @@ interface FormProps {
|
|||||||
onFinish: (obj: DeviceKeys) => void;
|
onFinish: (obj: DeviceKeys) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class LW10DeviceKeysForm extends Component<FormProps> {
|
function LW10DeviceKeysForm(props: FormProps) {
|
||||||
formRef = React.createRef<any>();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
onFinish = (values: DeviceKeys.AsObject) => {
|
const onFinish = (values: DeviceKeys.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let dk = new DeviceKeys();
|
let dk = new DeviceKeys();
|
||||||
|
|
||||||
dk.setDevEui(v.devEui);
|
dk.setDevEui(v.devEui);
|
||||||
@ -37,23 +37,16 @@ class LW10DeviceKeysForm extends Component<FormProps> {
|
|||||||
// the AppKey has been renamed to the NwkKey and a new value AppKey was added.
|
// the AppKey has been renamed to the NwkKey and a new value AppKey was added.
|
||||||
dk.setNwkKey(v.nwkKey);
|
dk.setNwkKey(v.nwkKey);
|
||||||
|
|
||||||
this.props.onFinish(dk);
|
props.onFinish(dk);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish} form={form}>
|
||||||
layout="vertical"
|
|
||||||
initialValues={this.props.initialValues.toObject()}
|
|
||||||
onFinish={this.onFinish}
|
|
||||||
ref={this.formRef}
|
|
||||||
>
|
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Application key"
|
label="Application key"
|
||||||
name="nwkKey"
|
name="nwkKey"
|
||||||
tooltip="For LoRaWAN 1.0 devices. In case your device supports LoRaWAN 1.1, update the device-profile first."
|
tooltip="For LoRaWAN 1.0 devices. In case your device supports LoRaWAN 1.1, update the device-profile first."
|
||||||
value={this.props.initialValues.getNwkKey()}
|
value={props.initialValues.getNwkKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
@ -64,44 +57,35 @@ class LW10DeviceKeysForm extends Component<FormProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class LW11DeviceKeysForm extends Component<FormProps> {
|
function LW11DeviceKeysForm(props: FormProps) {
|
||||||
formRef = React.createRef<any>();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
onFinish = (values: DeviceKeys.AsObject) => {
|
const onFinish = (values: DeviceKeys.AsObject) => {
|
||||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
const v = Object.assign(props.initialValues.toObject(), values);
|
||||||
let dk = new DeviceKeys();
|
let dk = new DeviceKeys();
|
||||||
|
|
||||||
dk.setDevEui(v.devEui);
|
dk.setDevEui(v.devEui);
|
||||||
dk.setAppKey(v.appKey);
|
dk.setAppKey(v.appKey);
|
||||||
dk.setNwkKey(v.nwkKey);
|
dk.setNwkKey(v.nwkKey);
|
||||||
|
|
||||||
this.props.onFinish(dk);
|
props.onFinish(dk);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form layout="vertical" initialValues={props.initialValues.toObject()} onFinish={onFinish} form={form}>
|
||||||
layout="vertical"
|
|
||||||
initialValues={this.props.initialValues.toObject()}
|
|
||||||
onFinish={this.onFinish}
|
|
||||||
ref={this.formRef}
|
|
||||||
>
|
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Application key"
|
label="Application key"
|
||||||
tooltip="For LoRaWAN 1.1 devices. In case your device does not support LoRaWAN 1.1, update the device-profile first."
|
tooltip="For LoRaWAN 1.1 devices. In case your device does not support LoRaWAN 1.1, update the device-profile first."
|
||||||
name="appKey"
|
name="appKey"
|
||||||
value={this.props.initialValues.getAppKey()}
|
value={props.initialValues.getAppKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<AesKeyInput
|
<AesKeyInput
|
||||||
label="Network key"
|
label="Network key"
|
||||||
tooltip="For LoRaWAN 1.1 devices. In case your device does not support LoRaWAN 1.1, update the device-profile first."
|
tooltip="For LoRaWAN 1.1 devices. In case your device does not support LoRaWAN 1.1, update the device-profile first."
|
||||||
name="nwkKey"
|
name="nwkKey"
|
||||||
value={this.props.initialValues.getNwkKey()}
|
value={props.initialValues.getNwkKey()}
|
||||||
formRef={this.formRef}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
@ -112,112 +96,93 @@ class LW11DeviceKeysForm extends Component<FormProps> {
|
|||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface IProps extends RouteComponentProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
application: Application;
|
application: Application;
|
||||||
device: Device;
|
device: Device;
|
||||||
deviceProfile: DeviceProfile;
|
deviceProfile: DeviceProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
function SetDeviceKeys(props: IProps) {
|
||||||
deviceKeys?: DeviceKeys;
|
const navigate = useNavigate();
|
||||||
deviceKeysRequested: boolean;
|
const [deviceKeys, setDeviceKeys] = useState<DeviceKeys | undefined>(undefined);
|
||||||
}
|
const [deviceKeysRequested, setDeviceKeysRequested] = useState<boolean>(false);
|
||||||
|
|
||||||
class SetDeviceKeys extends Component<IProps, IState> {
|
useEffect(() => {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
deviceKeysRequested: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.getDeviceKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeviceKeys = () => {
|
|
||||||
let req = new GetDeviceKeysRequest();
|
let req = new GetDeviceKeysRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
|
|
||||||
DeviceStore.getKeys(req, (resp?: GetDeviceKeysResponse) => {
|
DeviceStore.getKeys(req, (resp?: GetDeviceKeysResponse) => {
|
||||||
if (resp) {
|
if (resp) {
|
||||||
this.setState({
|
setDeviceKeys(resp.getDeviceKeys());
|
||||||
deviceKeys: resp.getDeviceKeys(),
|
setDeviceKeysRequested(true);
|
||||||
deviceKeysRequested: true,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
setDeviceKeysRequested(true);
|
||||||
deviceKeysRequested: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}, [props]);
|
||||||
|
|
||||||
onFinish = (obj: DeviceKeys) => {
|
const onFinish = (obj: DeviceKeys) => {
|
||||||
if (this.state.deviceKeys) {
|
if (deviceKeys) {
|
||||||
// this is an update
|
// this is an update
|
||||||
let req = new UpdateDeviceKeysRequest();
|
let req = new UpdateDeviceKeysRequest();
|
||||||
req.setDeviceKeys(obj);
|
req.setDeviceKeys(obj);
|
||||||
|
|
||||||
DeviceStore.updateKeys(req, () => {
|
DeviceStore.updateKeys(req, () => {
|
||||||
this.props.history.push(
|
navigate(
|
||||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${this.props.device.getDevEui()}`,
|
`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}/devices/${props.device.getDevEui()}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// this is a create
|
// this is a create
|
||||||
let req = new CreateDeviceKeysRequest();
|
let req = new CreateDeviceKeysRequest();
|
||||||
obj.setDevEui(this.props.device.getDevEui());
|
obj.setDevEui(props.device.getDevEui());
|
||||||
req.setDeviceKeys(obj);
|
req.setDeviceKeys(obj);
|
||||||
|
|
||||||
DeviceStore.createKeys(req, () => {
|
DeviceStore.createKeys(req, () => {
|
||||||
this.props.history.push(
|
navigate(
|
||||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${this.props.device.getDevEui()}`,
|
`/tenants/${props.tenant.getId()}/applications/${props.application.getId()}/devices/${props.device.getDevEui()}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
flushDevNonces = () => {
|
const flushDevNonces = () => {
|
||||||
let req = new FlushDevNoncesRequest();
|
let req = new FlushDevNoncesRequest();
|
||||||
req.setDevEui(this.props.device.getDevEui());
|
req.setDevEui(props.device.getDevEui());
|
||||||
DeviceStore.flushDevNonces(req, () => {});
|
DeviceStore.flushDevNonces(req, () => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
if (!deviceKeysRequested) {
|
||||||
if (!this.state.deviceKeysRequested) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const macVersion = this.props.deviceProfile.getMacVersion();
|
const macVersion = props.deviceProfile.getMacVersion();
|
||||||
const lw11 = macVersion === MacVersion.LORAWAN_1_1_0;
|
const lw11 = macVersion === MacVersion.LORAWAN_1_1_0;
|
||||||
|
|
||||||
let initialValues = new DeviceKeys();
|
let initialValues = new DeviceKeys();
|
||||||
if (this.state.deviceKeys) {
|
if (deviceKeys) {
|
||||||
initialValues = this.state.deviceKeys;
|
initialValues = deviceKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||||
{this.state.deviceKeys && (
|
{deviceKeys && (
|
||||||
<div style={{ float: "right" }}>
|
<div style={{ float: "right" }}>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
placement="left"
|
placement="left"
|
||||||
title="Are you sure you want to flush all device-nonces that have been used during previous OTAA activations?"
|
title="Are you sure you want to flush all device-nonces that have been used during previous OTAA activations?"
|
||||||
onConfirm={this.flushDevNonces}
|
onConfirm={flushDevNonces}
|
||||||
>
|
>
|
||||||
<Button>Flush OTAA device nonces</Button>
|
<Button>Flush OTAA device nonces</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!lw11 && <LW10DeviceKeysForm initialValues={initialValues} onFinish={this.onFinish} />}
|
{!lw11 && <LW10DeviceKeysForm initialValues={initialValues} onFinish={onFinish} />}
|
||||||
{lw11 && <LW11DeviceKeysForm initialValues={initialValues} onFinish={this.onFinish} />}
|
{lw11 && <LW11DeviceKeysForm initialValues={initialValues} onFinish={onFinish} />}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default SetDeviceKeys;
|
export default SetDeviceKeys;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user