mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-05-18 16:53:16 +00:00
Implement automatic formatting for JS / TSX source code.
Implemented automatic code formatting for JS/TSX using prettier and husky with pre-commit hook Signed-off-by: SAGAR PATEL <sagar.a.patel@slscorp.com> Co-authored-by: Orne Brocaar <info@brocaar.com>
This commit is contained in:
parent
d8377cd26a
commit
2ea86b2ca2
2
ui/.prettierignore
Normal file
2
ui/.prettierignore
Normal file
@ -0,0 +1,2 @@
|
||||
build
|
||||
node_modules
|
13
ui/.prettierrc.json
Normal file
13
ui/.prettierrc.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"endOfLine": "auto",
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"printWidth": 120,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"jsxSingleQuote": false,
|
||||
"quoteProps": "as-needed",
|
||||
"jsxBracketSameLine": true
|
||||
}
|
@ -44,7 +44,14 @@
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "prettier --check .",
|
||||
"format": "prettier --write . && git add -A ."
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "yarn format"
|
||||
}
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
@ -60,5 +67,9 @@
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"proxy": "http://127.0.0.1:8080/"
|
||||
"proxy": "http://127.0.0.1:8080/",
|
||||
"devDependencies": {
|
||||
"husky": "^7.0.4",
|
||||
"prettier": "^2.6.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import { Router, Route, Switch } from "react-router-dom";
|
||||
|
||||
import { Layout } from 'antd';
|
||||
import { Layout } from "antd";
|
||||
|
||||
import { User } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
|
||||
|
||||
@ -35,15 +35,12 @@ import SessionStore from "./stores/SessionStore";
|
||||
|
||||
import history from "./history";
|
||||
|
||||
|
||||
interface IProps {
|
||||
}
|
||||
interface IProps {}
|
||||
|
||||
interface IState {
|
||||
user?: User;
|
||||
}
|
||||
|
||||
|
||||
class App extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -66,13 +63,14 @@ class App extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Layout style={{minHeight:"100vh"}}>
|
||||
return (
|
||||
<Layout style={{ minHeight: "100vh" }}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route exact path="/" component={TenantRedirect} />
|
||||
<Route exact path="/login" component={Login} />
|
||||
{this.state.user && <Route>
|
||||
<Route exact path="/" component={TenantRedirect} />
|
||||
<Route exact path="/login" component={Login} />
|
||||
{this.state.user && (
|
||||
<Route>
|
||||
<Layout.Header className="layout-header">
|
||||
<Header user={this.state.user} />
|
||||
</Layout.Header>
|
||||
@ -80,7 +78,7 @@ class App extends Component<IProps, IState> {
|
||||
<Layout.Sider width="300" theme="light" className="layout-menu">
|
||||
<Menu />
|
||||
</Layout.Sider>
|
||||
<Layout.Content className="layout-content" style={{ padding: '24px 24px 24px' }}>
|
||||
<Layout.Content className="layout-content" style={{ padding: "24px 24px 24px" }}>
|
||||
<Switch>
|
||||
<Route exact path="/dashboard" component={Dashboard} />
|
||||
|
||||
@ -98,7 +96,8 @@ class App extends Component<IProps, IState> {
|
||||
</Switch>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
</Route>}
|
||||
</Route>
|
||||
)}
|
||||
</Switch>
|
||||
</Router>
|
||||
</Layout>
|
||||
|
@ -2,7 +2,6 @@ import { Component } from "react";
|
||||
|
||||
import SessionStore from "../stores/SessionStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
tenantId?: string;
|
||||
isDeviceAdmin?: boolean;
|
||||
@ -68,14 +67,14 @@ class Admin extends Component<IProps, IState> {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.admin) {
|
||||
return(this.props.children);
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
return(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@ import { Input, Select, Button, Space, Form } from "antd";
|
||||
import { ReloadOutlined } from "@ant-design/icons";
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -18,7 +18,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class AesKeyInput extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -39,7 +38,7 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value) {
|
||||
@ -62,10 +61,13 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: value,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
onByteOrderSelect = (v: string) => {
|
||||
if (v === this.state.byteOrder) {
|
||||
@ -79,21 +81,27 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
const current = this.state.value;
|
||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||
|
||||
this.setState({
|
||||
value: bytes.reverse().join(""),
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: bytes.reverse().join(""),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
generateRandom = () => {
|
||||
let cryptoObj = window.crypto || window.Crypto;
|
||||
let b = new Uint8Array(16);
|
||||
cryptoObj.getRandomValues(b);
|
||||
|
||||
let key = Buffer.from(b).toString('hex');
|
||||
this.setState({
|
||||
value: key,
|
||||
}, this.updateField);
|
||||
}
|
||||
let key = Buffer.from(b).toString("hex");
|
||||
this.setState(
|
||||
{
|
||||
value: key,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const addon = (
|
||||
@ -102,23 +110,34 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
<Select.Option value="msb">MSB</Select.Option>
|
||||
<Select.Option value="lsb">LSB</Select.Option>
|
||||
</Select>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
||||
<ReloadOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{32}/g),
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{32}/g),
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
tooltip={this.props.tooltip}
|
||||
>
|
||||
<Input hidden />
|
||||
<Input.Password id={`${this.props.name}Render`} onChange={this.onChange} addonAfter={!this.props.disabled && addon} style={{fontFamily: "monospace"}} value={this.state.value} disabled={this.props.disabled} />
|
||||
<Input.Password
|
||||
id={`${this.props.name}Render`}
|
||||
onChange={this.onChange}
|
||||
addonAfter={!this.props.disabled && addon}
|
||||
style={{ fontFamily: "monospace" }}
|
||||
value={this.state.value}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
@ -2,24 +2,23 @@ import React, { Component } from "react";
|
||||
|
||||
import { Select } from "antd";
|
||||
|
||||
export type OptionsCallbackFunc = (o: {label: string, value: string}[]) => void;
|
||||
export type OptionCallbackFunc = (o: {label: string, value: string}) => void;
|
||||
export type OptionsCallbackFunc = (o: { label: string; value: string }[]) => void;
|
||||
export type OptionCallbackFunc = (o: { label: string; value: string }) => void;
|
||||
|
||||
interface IProps {
|
||||
placeholder: string;
|
||||
className: string;
|
||||
value?: string,
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void,
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void,
|
||||
onSelect?: (s: string) => void,
|
||||
value?: string;
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void;
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void;
|
||||
onSelect?: (s: string) => void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
option?: {label: string, value: string};
|
||||
options: {label: string, value: string}[];
|
||||
option?: { label: string; value: string };
|
||||
options: { label: string; value: string }[];
|
||||
}
|
||||
|
||||
|
||||
class Autocomplete extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -31,7 +30,7 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value && this.props.value !== "") {
|
||||
this.props.getOption(this.props.value, (o: {label: string, value: string}) => {
|
||||
this.props.getOption(this.props.value, (o: { label: string; value: string }) => {
|
||||
this.setState({
|
||||
options: [o],
|
||||
});
|
||||
@ -45,7 +44,7 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
if (this.props.value && this.props.value !== "") {
|
||||
this.props.getOption(this.props.value, (o: {label: string, value: string}) => {
|
||||
this.props.getOption(this.props.value, (o: { label: string; value: string }) => {
|
||||
this.setState({
|
||||
options: [o],
|
||||
});
|
||||
@ -54,7 +53,7 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
onFocus = () => {
|
||||
this.props.getOptions("", (options) => {
|
||||
this.props.getOptions("", options => {
|
||||
if (this.state.option !== undefined) {
|
||||
const selected = this.state.option.value;
|
||||
|
||||
@ -67,30 +66,30 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
options: options,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSearch = (value: string) => {
|
||||
this.props.getOptions(value, (options) => {
|
||||
this.props.getOptions(value, options => {
|
||||
this.setState({
|
||||
options: options,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSelect = (value: string, option: any) => {
|
||||
this.setState({
|
||||
option: {label: option.label, value: option.value},
|
||||
option: { label: option.label, value: option.value },
|
||||
});
|
||||
|
||||
if (this.props.onSelect !== undefined) {
|
||||
this.props.onSelect(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { getOption, getOptions, ...otherProps } = this.props;
|
||||
|
||||
return(
|
||||
return (
|
||||
<Select
|
||||
showSearch
|
||||
options={this.state.options}
|
||||
|
@ -4,26 +4,26 @@ import { Form } from "antd";
|
||||
|
||||
import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "./Autocomplete";
|
||||
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void,
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void,
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void;
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void;
|
||||
}
|
||||
|
||||
|
||||
class AutocompleteInput extends Component<IProps> {
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please select a ${this.props.label}`,
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please select a ${this.props.label}`,
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
>
|
||||
|
@ -1,15 +1,14 @@
|
||||
import React, { Component } from "react";
|
||||
import {Controlled as CodeMirror} from "react-codemirror2";
|
||||
import { Controlled as CodeMirror } from "react-codemirror2";
|
||||
|
||||
import { Form } from "antd";
|
||||
|
||||
import "codemirror/mode/javascript/javascript";
|
||||
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label?: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label?: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -20,7 +19,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class CodeEditor extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -43,13 +41,16 @@ class CodeEditor extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = (editor: any, data: any, newCode: string) => {
|
||||
this.setState({
|
||||
value: newCode,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: newCode,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const codeMirrorOptions = {
|
||||
@ -60,21 +61,13 @@ class CodeEditor extends Component<IProps, IState> {
|
||||
};
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
tooltip={this.props.tooltip}
|
||||
>
|
||||
<div style={{border: "1px solid #cccccc"}}>
|
||||
<CodeMirror
|
||||
value={this.state.value}
|
||||
options={codeMirrorOptions}
|
||||
onBeforeChange={this.handleChange}
|
||||
/>
|
||||
<Form.Item label={this.props.label} name={this.props.name} tooltip={this.props.tooltip}>
|
||||
<div style={{ border: "1px solid #cccccc" }}>
|
||||
<CodeMirror value={this.state.value} options={codeMirrorOptions} onBeforeChange={this.handleChange} />
|
||||
</div>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CodeEditor
|
||||
export default CodeEditor;
|
||||
|
@ -5,10 +5,8 @@ import { ColumnsType } from "antd/es/table";
|
||||
|
||||
import SessionStore from "../stores/SessionStore";
|
||||
|
||||
|
||||
export type GetPageCallbackFunc = (totalCount: number, rows: object[]) => void;
|
||||
|
||||
|
||||
interface IProps {
|
||||
columns: ColumnsType<any>;
|
||||
getPage: (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => void;
|
||||
@ -23,10 +21,9 @@ interface IState {
|
||||
pageSize: number;
|
||||
currentPage: number;
|
||||
rows: object[];
|
||||
loading: boolean,
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
|
||||
class DataTable extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -53,37 +50,40 @@ class DataTable extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
onChangePage = (page: number, pageSize?: number | void) => {
|
||||
this.setState({
|
||||
loading: true,
|
||||
}, () => {
|
||||
let pz = pageSize;
|
||||
if (!pz) {
|
||||
pz = this.state.pageSize;
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
loading: true,
|
||||
},
|
||||
() => {
|
||||
let pz = pageSize;
|
||||
if (!pz) {
|
||||
pz = this.state.pageSize;
|
||||
}
|
||||
|
||||
this.props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => {
|
||||
this.setState({
|
||||
currentPage: page,
|
||||
totalCount: totalCount,
|
||||
rows: rows,
|
||||
pageSize: pz || 0,
|
||||
loading: false,
|
||||
this.props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => {
|
||||
this.setState({
|
||||
currentPage: page,
|
||||
totalCount: totalCount,
|
||||
rows: rows,
|
||||
pageSize: pz || 0,
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
onShowSizeChange = (page: number, pageSize: number) => {
|
||||
this.onChangePage(page, pageSize);
|
||||
SessionStore.setRowsPerPage(pageSize);
|
||||
}
|
||||
};
|
||||
|
||||
onRowsSelectChange = (ids: React.Key[]) => {
|
||||
const idss = ids as string[];
|
||||
if (this.props.onRowsSelectChange) {
|
||||
this.props.onRowsSelectChange(idss);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { getPage, refreshKey, ...otherProps } = this.props;
|
||||
@ -97,12 +97,12 @@ class DataTable extends Component<IProps, IState> {
|
||||
let pagination = undefined;
|
||||
if (this.props.noPagination === undefined || this.props.noPagination === false) {
|
||||
pagination = {
|
||||
current: this.state.currentPage,
|
||||
total: this.state.totalCount,
|
||||
pageSize: this.state.pageSize,
|
||||
onChange: this.onChangePage,
|
||||
showSizeChanger: true,
|
||||
onShowSizeChange: this.onShowSizeChange,
|
||||
current: this.state.currentPage,
|
||||
total: this.state.totalCount,
|
||||
pageSize: this.state.pageSize,
|
||||
onChange: this.onChangePage,
|
||||
showSizeChanger: true,
|
||||
onShowSizeChange: this.onShowSizeChange,
|
||||
};
|
||||
}
|
||||
|
||||
@ -113,8 +113,7 @@ class DataTable extends Component<IProps, IState> {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return(
|
||||
return (
|
||||
<Table
|
||||
loading={loadingProps}
|
||||
dataSource={this.state.rows}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
import { Popover, Button, Typography, Space, Input } from 'antd';
|
||||
|
||||
import { Popover, Button, Typography, Space, Input } from "antd";
|
||||
|
||||
interface IProps {
|
||||
typ: string;
|
||||
@ -9,17 +8,15 @@ interface IProps {
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
|
||||
interface ConfirmState {
|
||||
confirm: string;
|
||||
}
|
||||
|
||||
|
||||
class DeleteConfirmContent extends Component<IProps, ConfirmState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
confirm: '',
|
||||
confirm: "",
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,24 +24,31 @@ class DeleteConfirmContent extends Component<IProps, ConfirmState> {
|
||||
this.setState({
|
||||
confirm: e.target.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<Typography.Text>Enter '{this.props.confirm}' to confirm you want to delete this {this.props.typ}:</Typography.Text>
|
||||
<Input placeholder={this.props.confirm} onChange={this.onChange} />
|
||||
<Button onClick={this.props.onConfirm} disabled={this.state.confirm !== this.props.confirm} style={{float: "right"}}>Delete</Button>
|
||||
<Typography.Text>
|
||||
Enter '{this.props.confirm}' to confirm you want to delete this {this.props.typ}:
|
||||
</Typography.Text>
|
||||
<Input placeholder={this.props.confirm} onChange={this.onChange} />
|
||||
<Button
|
||||
onClick={this.props.onConfirm}
|
||||
disabled={this.state.confirm !== this.props.confirm}
|
||||
style={{ float: "right" }}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DeleteConfirm extends Component<IProps> {
|
||||
render() {
|
||||
return(
|
||||
<Popover content={<DeleteConfirmContent {...this.props}/>} trigger="click" placement="left">
|
||||
return (
|
||||
<Popover content={<DeleteConfirmContent {...this.props} />} trigger="click" placement="left">
|
||||
{this.props.children}
|
||||
</Popover>
|
||||
);
|
||||
|
@ -3,21 +3,15 @@ import React, { Component } from "react";
|
||||
import { Input, Select, Button, Space, Form } from "antd";
|
||||
import { ReloadOutlined } from "@ant-design/icons";
|
||||
|
||||
|
||||
import {
|
||||
GetRandomDevAddrRequest,
|
||||
GetRandomDevAddrResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
|
||||
import { GetRandomDevAddrRequest, GetRandomDevAddrResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
|
||||
import DeviceStore from "../stores/DeviceStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
devEui: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
devEui: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -28,7 +22,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class DevAddrInput extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -49,7 +42,7 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value) {
|
||||
@ -72,10 +65,13 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: value,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
onByteOrderSelect = (v: string) => {
|
||||
if (v === this.state.byteOrder) {
|
||||
@ -89,21 +85,27 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
const current = this.state.value;
|
||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||
|
||||
this.setState({
|
||||
value: bytes.reverse().join(""),
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: bytes.reverse().join(""),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
generateRandom = () => {
|
||||
let req = new GetRandomDevAddrRequest();
|
||||
req.setDevEui(this.props.devEui);
|
||||
|
||||
DeviceStore.getRandomDevAddr(req, (resp: GetRandomDevAddrResponse) => {
|
||||
this.setState({
|
||||
value: resp.getDevAddr(),
|
||||
}, this.updateField);
|
||||
this.setState(
|
||||
{
|
||||
value: resp.getDevAddr(),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const addon = (
|
||||
@ -112,22 +114,33 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
<Select.Option value="msb">MSB</Select.Option>
|
||||
<Select.Option value="lsb">LSB</Select.Option>
|
||||
</Select>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
||||
<ReloadOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
>
|
||||
<Input hidden />
|
||||
<Input id={`${this.props.name}Render`} onChange={this.onChange} addonAfter={!this.props.disabled && addon} style={{fontFamily: "monospace"}} value={this.state.value} disabled={this.props.disabled} />
|
||||
<Input
|
||||
id={`${this.props.name}Render`}
|
||||
onChange={this.onChange}
|
||||
addonAfter={!this.props.disabled && addon}
|
||||
style={{ fontFamily: "monospace" }}
|
||||
value={this.state.value}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ import { Input, Select, Button, Space, Form } from "antd";
|
||||
import { ReloadOutlined } from "@ant-design/icons";
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -17,7 +17,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class EuiInput extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -38,7 +37,7 @@ class EuiInput extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value) {
|
||||
@ -61,10 +60,13 @@ class EuiInput extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: value,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
onByteOrderSelect = (v: string) => {
|
||||
if (v === this.state.byteOrder) {
|
||||
@ -78,21 +80,27 @@ class EuiInput extends Component<IProps, IState> {
|
||||
const current = this.state.value;
|
||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||
|
||||
this.setState({
|
||||
value: bytes.reverse().join(""),
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: bytes.reverse().join(""),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
generateRandom = () => {
|
||||
let cryptoObj = window.crypto || window.Crypto;
|
||||
let b = new Uint8Array(8);
|
||||
cryptoObj.getRandomValues(b);
|
||||
|
||||
let key = Buffer.from(b).toString('hex');
|
||||
this.setState({
|
||||
value: key,
|
||||
}, this.updateField);
|
||||
}
|
||||
let key = Buffer.from(b).toString("hex");
|
||||
this.setState(
|
||||
{
|
||||
value: key,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const addon = (
|
||||
@ -101,22 +109,33 @@ class EuiInput extends Component<IProps, IState> {
|
||||
<Select.Option value="msb">MSB</Select.Option>
|
||||
<Select.Option value="lsb">LSB</Select.Option>
|
||||
</Select>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
||||
<ReloadOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{16}/g),
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{16}/g),
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
>
|
||||
<Input hidden />
|
||||
<Input id={`${this.props.name}Render`} onChange={this.onChange} addonAfter={!this.props.disabled && addon} style={{fontFamily: "monospace"}} value={this.state.value} disabled={this.props.disabled} />
|
||||
<Input
|
||||
id={`${this.props.name}Render`}
|
||||
onChange={this.onChange}
|
||||
addonAfter={!this.props.disabled && addon}
|
||||
style={{ fontFamily: "monospace" }}
|
||||
value={this.state.value}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
@ -13,27 +13,20 @@ import {
|
||||
|
||||
import InternalStore from "../stores/InternalStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
user: User;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
searchResult?: GlobalSearchResponse;
|
||||
settings?: SettingsResponse,
|
||||
settings?: SettingsResponse;
|
||||
}
|
||||
|
||||
const renderTitle = (title: string) => (
|
||||
<span>
|
||||
{title}
|
||||
</span>
|
||||
);
|
||||
const renderTitle = (title: string) => <span>{title}</span>;
|
||||
|
||||
const renderItem = (title: string, url: string) => ({
|
||||
value: title,
|
||||
label: (
|
||||
<Link to={url}>{title}</Link>
|
||||
),
|
||||
label: <Link to={url}>{title}</Link>,
|
||||
});
|
||||
|
||||
class Header extends Component<IProps, IState> {
|
||||
@ -65,7 +58,7 @@ class Header extends Component<IProps, IState> {
|
||||
searchResult: resp,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.settings === undefined) {
|
||||
@ -76,9 +69,11 @@ class Header extends Component<IProps, IState> {
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
{!oidcEnabled && <Menu.Item>
|
||||
<Link to={`/users/${this.props.user.getId()}/password`}>Change password</Link>
|
||||
</Menu.Item>}
|
||||
{!oidcEnabled && (
|
||||
<Menu.Item>
|
||||
<Link to={`/users/${this.props.user.getId()}/password`}>Change password</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item>
|
||||
<Link to="/login">Logout</Link>
|
||||
</Menu.Item>
|
||||
@ -90,19 +85,19 @@ class Header extends Component<IProps, IState> {
|
||||
options: any[];
|
||||
}[] = [
|
||||
{
|
||||
label: renderTitle('Tenants'),
|
||||
label: renderTitle("Tenants"),
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
label: renderTitle('Gateways'),
|
||||
label: renderTitle("Gateways"),
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
label: renderTitle('Applications'),
|
||||
label: renderTitle("Applications"),
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
label: renderTitle('Devices'),
|
||||
label: renderTitle("Devices"),
|
||||
options: [],
|
||||
},
|
||||
];
|
||||
@ -110,24 +105,36 @@ class Header extends Component<IProps, IState> {
|
||||
if (this.state.searchResult !== undefined) {
|
||||
for (const res of this.state.searchResult.getResultList()) {
|
||||
if (res.getKind() === "tenant") {
|
||||
options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`))
|
||||
options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`));
|
||||
}
|
||||
|
||||
if (res.getKind() === "gateway") {
|
||||
options[1].options.push(renderItem(res.getGatewayName(), `/tenants/${res.getTenantId()}/gateways/${res.getGatewayId()}`))
|
||||
options[1].options.push(
|
||||
renderItem(res.getGatewayName(), `/tenants/${res.getTenantId()}/gateways/${res.getGatewayId()}`),
|
||||
);
|
||||
}
|
||||
|
||||
if (res.getKind() === "application") {
|
||||
options[2].options.push(renderItem(res.getApplicationName(), `/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}`))
|
||||
options[2].options.push(
|
||||
renderItem(
|
||||
res.getApplicationName(),
|
||||
`/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (res.getKind() === "device") {
|
||||
options[3].options.push(renderItem(res.getDeviceName(), `/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}/devices/${res.getDeviceDevEui()}`));
|
||||
options[3].options.push(
|
||||
renderItem(
|
||||
res.getDeviceName(),
|
||||
`/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}/devices/${res.getDeviceDevEui()}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<img className="logo" alt="ChirpStack" src="/logo.png" />
|
||||
<div className="actions">
|
||||
@ -138,15 +145,19 @@ class Header extends Component<IProps, IState> {
|
||||
options={options}
|
||||
onSearch={this.onSearch}
|
||||
>
|
||||
<Input.Search placeholder="Search..." style={{width: 500, marginTop: -5}} />
|
||||
<Input.Search placeholder="Search..." style={{ width: 500, marginTop: -5 }} />
|
||||
</AutoComplete>
|
||||
</div>
|
||||
<div className="help">
|
||||
<a href="https://www.chirpstack.io" target="_blank" rel="noreferrer"><Button icon={<QuestionOutlined />}/></a>
|
||||
<a href="https://www.chirpstack.io" target="_blank" rel="noreferrer">
|
||||
<Button icon={<QuestionOutlined />} />
|
||||
</a>
|
||||
</div>
|
||||
<div className="user">
|
||||
<Dropdown overlay={menu} placement="bottomRight" trigger={['click']}>
|
||||
<Button type="primary" icon={<UserOutlined />}>{this.props.user.getEmail()} <DownOutlined /></Button>
|
||||
<Dropdown overlay={menu} placement="bottomRight" trigger={["click"]}>
|
||||
<Button type="primary" icon={<UserOutlined />}>
|
||||
{this.props.user.getEmail()} <DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,28 +3,25 @@ import React, { Component } from "react";
|
||||
import { color } from "chart.js/helpers";
|
||||
import { Chart } from "react-chartjs-2";
|
||||
|
||||
|
||||
interface HeatmapData {
|
||||
x: string;
|
||||
y: Array<[string, number]>;
|
||||
}
|
||||
|
||||
|
||||
interface IProps {
|
||||
data: HeatmapData[];
|
||||
fromColor: string;
|
||||
toColor: string;
|
||||
}
|
||||
|
||||
|
||||
class Heatmap extends Component<IProps> {
|
||||
render() {
|
||||
if (this.props.data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let xSet: {[key: string]: any} = {};
|
||||
let ySet: {[key: string]: any} = {};
|
||||
let xSet: { [key: string]: any } = {};
|
||||
let ySet: { [key: string]: any } = {};
|
||||
|
||||
let dataData: {
|
||||
x: string;
|
||||
@ -45,18 +42,22 @@ class Heatmap extends Component<IProps> {
|
||||
fromColor: this.props.fromColor.match(/\d+/g)!.map(Number),
|
||||
toColor: this.props.toColor.match(/\d+/g)!.map(Number),
|
||||
backgroundColor: (ctx: any): string => {
|
||||
if (ctx.dataset === undefined || ctx.dataset.data === undefined || ctx.dataset.data[ctx.dataIndex] === undefined) {
|
||||
return color('white').rgbString();
|
||||
if (
|
||||
ctx.dataset === undefined ||
|
||||
ctx.dataset.data === undefined ||
|
||||
ctx.dataset.data[ctx.dataIndex] === undefined
|
||||
) {
|
||||
return color("white").rgbString();
|
||||
}
|
||||
|
||||
const value = ctx.dataset.data[ctx.dataIndex].v;
|
||||
const steps = ctx.dataset.maxValue - ctx.dataset.minValue + 1;
|
||||
const step = value - ctx.dataset.minValue;
|
||||
const factor = 1 / steps * step;
|
||||
const factor = (1 / steps) * step;
|
||||
|
||||
let result: [number, number, number] = ctx.dataset.fromColor.slice();
|
||||
for (var i = 0; i < 3; i++) {
|
||||
result[i] = Math.round(result[i] + factor * (ctx.dataset.toColor[i] - ctx.dataset.fromColor[i]));
|
||||
result[i] = Math.round(result[i] + factor * (ctx.dataset.toColor[i] - ctx.dataset.fromColor[i]));
|
||||
}
|
||||
|
||||
return color(result).rgbString();
|
||||
@ -94,19 +95,18 @@ class Heatmap extends Component<IProps> {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: {display: false},
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
title: () => {
|
||||
return '';
|
||||
return "";
|
||||
},
|
||||
label: (ctx: any) => {
|
||||
const v = ctx.dataset.data[ctx.dataIndex].v;
|
||||
return 'Count: ' + v;
|
||||
return "Count: " + v;
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -136,9 +136,7 @@ class Heatmap extends Component<IProps> {
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<Chart type="matrix" data={data} options={options} />
|
||||
);
|
||||
return <Chart type="matrix" data={data} options={options} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,10 @@ import moment from "moment";
|
||||
import JSONTreeOriginal from "react-json-tree";
|
||||
|
||||
import { Tag, Drawer, Button, Table, Spin } from "antd";
|
||||
import { ZoomInOutlined } from '@ant-design/icons';
|
||||
import { ZoomInOutlined } from "@ant-design/icons";
|
||||
|
||||
import { LogItem } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
|
||||
|
||||
interface IProps {
|
||||
logs: LogItem[];
|
||||
}
|
||||
@ -15,8 +14,7 @@ interface IProps {
|
||||
interface IState {
|
||||
drawerOpen: boolean;
|
||||
body: any;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class LogTable extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
@ -32,7 +30,7 @@ class LogTable extends Component<IProps, IState> {
|
||||
this.setState({
|
||||
drawerOpen: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onDrawerOpen = (body: any) => {
|
||||
return () => {
|
||||
@ -41,44 +39,56 @@ class LogTable extends Component<IProps, IState> {
|
||||
drawerOpen: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let items = this.props.logs.map((l, i) => l.toObject());
|
||||
let body = JSON.parse(this.state.body);
|
||||
|
||||
const theme = {
|
||||
scheme: 'google',
|
||||
author: 'seth wright (http://sethawright.com)',
|
||||
base00: '#000000',
|
||||
base01: '#282a2e',
|
||||
base02: '#373b41',
|
||||
base03: '#969896',
|
||||
base04: '#b4b7b4',
|
||||
base05: '#c5c8c6',
|
||||
base06: '#e0e0e0',
|
||||
base07: '#ffffff',
|
||||
base08: '#CC342B',
|
||||
base09: '#F96A38',
|
||||
base0A: '#FBA922',
|
||||
base0B: '#198844',
|
||||
base0C: '#3971ED',
|
||||
base0D: '#3971ED',
|
||||
base0E: '#A36AC7',
|
||||
base0F: '#3971ED',
|
||||
scheme: "google",
|
||||
author: "seth wright (http://sethawright.com)",
|
||||
base00: "#000000",
|
||||
base01: "#282a2e",
|
||||
base02: "#373b41",
|
||||
base03: "#969896",
|
||||
base04: "#b4b7b4",
|
||||
base05: "#c5c8c6",
|
||||
base06: "#e0e0e0",
|
||||
base07: "#ffffff",
|
||||
base08: "#CC342B",
|
||||
base09: "#F96A38",
|
||||
base0A: "#FBA922",
|
||||
base0B: "#198844",
|
||||
base0C: "#3971ED",
|
||||
base0D: "#3971ED",
|
||||
base0E: "#A36AC7",
|
||||
base0F: "#3971ED",
|
||||
};
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<Drawer title="Details" placement="right" width={650} onClose={this.onDrawerClose} visible={this.state.drawerOpen}>
|
||||
<Drawer
|
||||
title="Details"
|
||||
placement="right"
|
||||
width={650}
|
||||
onClose={this.onDrawerClose}
|
||||
visible={this.state.drawerOpen}
|
||||
>
|
||||
<JSONTreeOriginal
|
||||
data={body}
|
||||
theme={theme}
|
||||
hideRoot={true}
|
||||
shouldExpandNode={() => {return true}}
|
||||
shouldExpandNode={() => {
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
{items.length !== 0 && <div className="spinner"><Spin /></div>}
|
||||
{items.length !== 0 && (
|
||||
<div className="spinner">
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
<Table
|
||||
showHeader={false}
|
||||
loading={items.length === 0}
|
||||
@ -101,19 +111,36 @@ class LogTable extends Component<IProps, IState> {
|
||||
dataIndex: "description",
|
||||
key: "description",
|
||||
width: 200,
|
||||
render: (text, obj) => <Button icon={<ZoomInOutlined />} type="primary" shape="round" size="small" onClick={this.onDrawerOpen(obj.body)}>{text}</Button>,
|
||||
render: (text, obj) => (
|
||||
<Button
|
||||
icon={<ZoomInOutlined />}
|
||||
type="primary"
|
||||
shape="round"
|
||||
size="small"
|
||||
onClick={this.onDrawerOpen(obj.body)}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Properties",
|
||||
dataIndex: "properties",
|
||||
key: "properties",
|
||||
render: (text, obj) => obj.propertiesMap.map((p, i) => {
|
||||
if (p[1] !== "") {
|
||||
return <Tag><pre>{p[0]}: {p[1]}</pre></Tag>
|
||||
}
|
||||
render: (text, obj) =>
|
||||
obj.propertiesMap.map((p, i) => {
|
||||
if (p[1] !== "") {
|
||||
return (
|
||||
<Tag>
|
||||
<pre>
|
||||
{p[0]}: {p[1]}
|
||||
</pre>
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
return null;
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
@ -3,8 +3,7 @@ import React, { Component } from "react";
|
||||
import L, { LatLngTuple, FitBoundsOptions } from "leaflet";
|
||||
import "leaflet.awesome-markers";
|
||||
import { MarkerProps as LMarkerProps } from "react-leaflet";
|
||||
import { MapContainer, Marker as LMarker, TileLayer } from 'react-leaflet';
|
||||
|
||||
import { MapContainer, Marker as LMarker, TileLayer } from "react-leaflet";
|
||||
|
||||
interface IProps {
|
||||
height: number;
|
||||
@ -17,7 +16,6 @@ interface IState {
|
||||
map?: L.Map;
|
||||
}
|
||||
|
||||
|
||||
class Map extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -25,22 +23,25 @@ class Map extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.props.bounds !== undefined) {
|
||||
map.fitBounds(this.props.bounds, this.props.boundsOptions);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
componentDidUpdate(oldProps: IProps) {
|
||||
if (this.props === oldProps) {
|
||||
@ -63,7 +64,7 @@ class Map extends Component<IProps, IState> {
|
||||
height: this.props.height,
|
||||
};
|
||||
|
||||
return(
|
||||
return (
|
||||
<MapContainer
|
||||
bounds={this.props.bounds}
|
||||
boundsOptions={this.props.boundsOptions}
|
||||
@ -84,7 +85,17 @@ class Map extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
export type MarkerColor = "red" | "darkred" | "orange" | "green" | "darkgreen" | "blue" | "purple" | "darkpurple" | "cadetblue" | undefined;
|
||||
export type MarkerColor =
|
||||
| "red"
|
||||
| "darkred"
|
||||
| "orange"
|
||||
| "green"
|
||||
| "darkgreen"
|
||||
| "blue"
|
||||
| "purple"
|
||||
| "darkpurple"
|
||||
| "cadetblue"
|
||||
| undefined;
|
||||
|
||||
interface MarkerProps extends LMarkerProps {
|
||||
position: [number, number];
|
||||
@ -102,9 +113,10 @@ export class Marker extends Component<MarkerProps> {
|
||||
markerColor: color,
|
||||
});
|
||||
|
||||
|
||||
return(
|
||||
<LMarker icon={iconMarker} position={position} {...otherProps}>{this.props.children}</LMarker>
|
||||
return (
|
||||
<LMarker icon={iconMarker} position={position} {...otherProps}>
|
||||
{this.props.children}
|
||||
</LMarker>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,22 @@ import React, { Component } from "react";
|
||||
import { withRouter, RouteComponentProps, Link } from "react-router-dom";
|
||||
|
||||
import { Menu, MenuProps } from "antd";
|
||||
import { CloudOutlined, HomeOutlined, UserOutlined, DashboardOutlined, KeyOutlined, WifiOutlined, ControlOutlined, AppstoreOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
CloudOutlined,
|
||||
HomeOutlined,
|
||||
UserOutlined,
|
||||
DashboardOutlined,
|
||||
KeyOutlined,
|
||||
WifiOutlined,
|
||||
ControlOutlined,
|
||||
AppstoreOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
import { GetTenantResponse, ListTenantsRequest, ListTenantsResponse } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import {
|
||||
GetTenantResponse,
|
||||
ListTenantsRequest,
|
||||
ListTenantsResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
|
||||
import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "../components/Autocomplete";
|
||||
import TenantStore from "../stores/TenantStore";
|
||||
@ -48,7 +61,7 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
||||
this.setState({
|
||||
tenantId: SessionStore.getTenantId(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getTenantOptions = (search: string, fn: OptionsCallbackFunc) => {
|
||||
let req = new ListTenantsRequest();
|
||||
@ -56,85 +69,86 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
||||
req.setLimit(10);
|
||||
|
||||
TenantStore.list(req, (resp: ListTenantsResponse) => {
|
||||
const options = resp.getResultList().map((o, i) => {return {label: o.getName(), value: o.getId()}});
|
||||
const options = resp.getResultList().map((o, i) => {
|
||||
return { label: o.getName(), value: o.getId() };
|
||||
});
|
||||
fn(options);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getTenantOption = (id: string, fn: OptionCallbackFunc) => {
|
||||
TenantStore.get(id, (resp: GetTenantResponse) => {
|
||||
const tenant = resp.getTenant();
|
||||
if (tenant) {
|
||||
fn({label: tenant.getName(), value: tenant.getId()});
|
||||
fn({ label: tenant.getName(), value: tenant.getId() });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onTenantSelect = (value: string) => {
|
||||
SessionStore.setTenantId(value);
|
||||
this.props.history.push(`/tenants/${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
parseLocation = () => {
|
||||
const path = this.props.history.location.pathname;
|
||||
const tenantRe = /\/tenants\/([\w-]{36})/g;
|
||||
const match = tenantRe.exec(path);
|
||||
|
||||
if (match !== null && (this.state.tenantId !== match[1])) {
|
||||
if (match !== null && this.state.tenantId !== match[1]) {
|
||||
SessionStore.setTenantId(match[1]);
|
||||
}
|
||||
|
||||
// ns dashboard
|
||||
if (path === "/dashboard") {
|
||||
this.setState({selectedKey: "ns-dashboard"});
|
||||
this.setState({ selectedKey: "ns-dashboard" });
|
||||
}
|
||||
|
||||
// ns tenants
|
||||
if (/\/tenants(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||
this.setState({selectedKey: "ns-tenants"});
|
||||
this.setState({ selectedKey: "ns-tenants" });
|
||||
}
|
||||
|
||||
// ns tenants
|
||||
if (/\/users(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||
this.setState({selectedKey: "ns-users"});
|
||||
this.setState({ selectedKey: "ns-users" });
|
||||
}
|
||||
|
||||
// ns api keys
|
||||
if (/\/api-keys(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||
this.setState({selectedKey: "ns-api-keys"});
|
||||
this.setState({ selectedKey: "ns-api-keys" });
|
||||
}
|
||||
|
||||
|
||||
// tenant dashboard
|
||||
if (/\/tenants\/[\w-]{36}/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-dashboard"});
|
||||
this.setState({ selectedKey: "tenant-dashboard" });
|
||||
}
|
||||
|
||||
// tenant users
|
||||
if (/\/tenants\/[\w-]{36}\/users.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-users"});
|
||||
this.setState({ selectedKey: "tenant-users" });
|
||||
}
|
||||
|
||||
// tenant api-keys
|
||||
if (/\/tenants\/[\w-]{36}\/api-keys.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-api-keys"});
|
||||
this.setState({ selectedKey: "tenant-api-keys" });
|
||||
}
|
||||
|
||||
// tenant device-profiles
|
||||
if (/\/tenants\/[\w-]{36}\/device-profiles.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-device-profiles"});
|
||||
this.setState({ selectedKey: "tenant-device-profiles" });
|
||||
}
|
||||
|
||||
// tenant gateways
|
||||
if (/\/tenants\/[\w-]{36}\/gateways.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-gateways"});
|
||||
this.setState({ selectedKey: "tenant-gateways" });
|
||||
}
|
||||
|
||||
// tenant applications
|
||||
if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-applications"});
|
||||
this.setState({ selectedKey: "tenant-applications" });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const tenantId = this.state.tenantId;
|
||||
@ -170,7 +184,7 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
||||
});
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<Autocomplete
|
||||
placeholder="Select tenant"
|
||||
|
@ -1,2 +1,2 @@
|
||||
import { createHashHistory } from 'history';
|
||||
import { createHashHistory } from "history";
|
||||
export default createHashHistory();
|
||||
|
@ -1,88 +1,88 @@
|
||||
.layout {
|
||||
margin-left: 300px;
|
||||
margin-left: 300px;
|
||||
}
|
||||
|
||||
.layout-header {
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 10px 0px #ccc;
|
||||
z-index: 999;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 10px 0px #ccc;
|
||||
z-index: 999;
|
||||
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.layout-header .logo {
|
||||
float: left;
|
||||
margin-top: 16px;
|
||||
height: 32px;
|
||||
float: left;
|
||||
margin-top: 16px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.layout-header .actions {
|
||||
float: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.layout-header .actions > div {
|
||||
display: inline;
|
||||
padding-left: 24px;
|
||||
display: inline;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.layout-menu {
|
||||
padding-top: 85px;
|
||||
padding-top: 85px;
|
||||
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.layout-menu .organiation-select {
|
||||
margin-left: 24px;
|
||||
margin-bottom: 12px;
|
||||
width: 252px;
|
||||
margin-left: 24px;
|
||||
margin-bottom: 12px;
|
||||
width: 252px;
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
margin-top: 65px;
|
||||
margin-top: 65px;
|
||||
}
|
||||
|
||||
.layout-content .content-header > * {
|
||||
float: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.layout-content .content-header h3 {
|
||||
float: left;
|
||||
display: inline;
|
||||
float: left;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.layout-content .content {
|
||||
background: #ffffff;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.chart-doughtnut {
|
||||
max-width: 350;
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
display: block;
|
||||
max-width: 350;
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dashboard-chart > .ant-card-body {
|
||||
height: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.integration-card {
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.integration-card .ant-card-meta-description {
|
||||
min-height: 75px;
|
||||
min-height: 75px;
|
||||
}
|
||||
|
||||
.search-dropdown .ant-select-dropdown-menu-item-group-title {
|
||||
|
@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
|
||||
|
||||
// import { Chart, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, Title } 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 App from "./App";
|
||||
@ -23,7 +23,7 @@ ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
document.getElementById("root"),
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ReportHandler } from 'web-vitals';
|
||||
import { ReportHandler } from "web-vitals";
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
|
@ -2,4 +2,4 @@
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import "@testing-library/jest-dom";
|
||||
|
@ -64,7 +64,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class ApplicationStore extends EventEmitter {
|
||||
client: ApplicationServiceClient;
|
||||
|
||||
@ -87,7 +86,7 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (req: GetApplicationRequest, callbackFunc: (resp: GetApplicationResponse) => void) => {
|
||||
this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -98,10 +97,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
update = (req: UpdateApplicationRequest, callbackFunc: () => void) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -114,10 +113,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
delete = (req: DeleteApplicationRequest, callbackFunc: () => void) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -130,7 +129,7 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
list = (req: ListApplicationsRequest, callbackFunc: (resp: ListApplicationsResponse) => void) => {
|
||||
this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -141,7 +140,7 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
listIntegrations = (req: ListIntegrationsRequest, callbackFunc: (resp: ListIntegrationsResponse) => void) => {
|
||||
this.client.listIntegrations(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -151,11 +150,11 @@ class ApplicationStore extends EventEmitter {
|
||||
}
|
||||
|
||||
callbackFunc(resp);
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
createHttpIntegration = (req: CreateHttpIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createHttpIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createHttpIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -168,7 +167,7 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getHttpIntegration = (req: GetHttpIntegrationRequest, callbackFunc: (resp: GetHttpIntegrationResponse) => void) => {
|
||||
this.client.getHttpIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -179,10 +178,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateHttpIntegration = (req: UpdateHttpIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateHttpIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateHttpIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -195,10 +194,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteHttpIntegration = (req: DeleteHttpIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteHttpIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteHttpIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -212,10 +211,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createAwsSnsIntegration = (req: CreateAwsSnsIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createAwsSnsIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createAwsSnsIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -228,9 +227,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getAwsSnsIntegration = (req: GetAwsSnsIntegrationRequest, callbackFunc: (resp: GetAwsSnsIntegrationResponse) => void) => {
|
||||
getAwsSnsIntegration = (
|
||||
req: GetAwsSnsIntegrationRequest,
|
||||
callbackFunc: (resp: GetAwsSnsIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getAwsSnsIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -239,10 +241,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateAwsSnsIntegration = (req: UpdateAwsSnsIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateAwsSnsIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateAwsSnsIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -255,10 +257,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteAwsSnsIntegration = (req: DeleteAwsSnsIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteAwsSnsIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteAwsSnsIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -272,10 +274,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createAzureServiceBusIntegration = (req: CreateAzureServiceBusIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createAzureServiceBusIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -288,9 +290,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getAzureServiceBusIntegration = (req: GetAzureServiceBusIntegrationRequest, callbackFunc: (resp: GetAzureServiceBusIntegrationResponse) => void) => {
|
||||
getAzureServiceBusIntegration = (
|
||||
req: GetAzureServiceBusIntegrationRequest,
|
||||
callbackFunc: (resp: GetAzureServiceBusIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -299,10 +304,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateAzureServiceBusIntegration = (req: UpdateAzureServiceBusIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateAzureServiceBusIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -315,10 +320,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteAzureServiceBusIntegration = (req: DeleteAzureServiceBusIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteAzureServiceBusIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -332,10 +337,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createGcpPubSubIntegration = (req: CreateGcpPubSubIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createGcpPubSubIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createGcpPubSubIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -348,9 +353,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getGcpPubSubIntegration = (req: GetGcpPubSubIntegrationRequest, callbackFunc: (resp: GetGcpPubSubIntegrationResponse) => void) => {
|
||||
getGcpPubSubIntegration = (
|
||||
req: GetGcpPubSubIntegrationRequest,
|
||||
callbackFunc: (resp: GetGcpPubSubIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getGcpPubSubIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -359,10 +367,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateGcpPubSubIntegration = (req: UpdateGcpPubSubIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateGcpPubSubIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateGcpPubSubIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -375,10 +383,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteGcpPubSubIntegration = (req: DeleteGcpPubSubIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteGcpPubSubIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteGcpPubSubIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -392,10 +400,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createInfluxDbIntegration = (req: CreateInfluxDbIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createInfluxDbIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createInfluxDbIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -408,9 +416,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getInfluxDbIntegration = (req: GetInfluxDbIntegrationRequest, callbackFunc: (resp: GetInfluxDbIntegrationResponse) => void) => {
|
||||
getInfluxDbIntegration = (
|
||||
req: GetInfluxDbIntegrationRequest,
|
||||
callbackFunc: (resp: GetInfluxDbIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getInfluxDbIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -419,10 +430,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateInfluxDbIntegration = (req: UpdateInfluxDbIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateInfluxDbIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateInfluxDbIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -435,10 +446,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteInfluxDbIntegration = (req: DeleteInfluxDbIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteInfluxDbIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteInfluxDbIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -452,10 +463,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createMyDevicesIntegration = (req: CreateMyDevicesIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createMyDevicesIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createMyDevicesIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -468,9 +479,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getMyDevicesIntegration = (req: GetMyDevicesIntegrationRequest, callbackFunc: (resp: GetMyDevicesIntegrationResponse) => void) => {
|
||||
getMyDevicesIntegration = (
|
||||
req: GetMyDevicesIntegrationRequest,
|
||||
callbackFunc: (resp: GetMyDevicesIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getMyDevicesIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -479,10 +493,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateMyDevicesIntegration = (req: UpdateMyDevicesIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateMyDevicesIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateMyDevicesIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -495,10 +509,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteMyDevicesIntegration = (req: DeleteMyDevicesIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteMyDevicesIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteMyDevicesIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -512,10 +526,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createPilotThingsIntegration = (req: CreatePilotThingsIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createPilotThingsIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createPilotThingsIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -528,9 +542,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getPilotThingsIntegration = (req: GetPilotThingsIntegrationRequest, callbackFunc: (resp: GetPilotThingsIntegrationResponse) => void) => {
|
||||
getPilotThingsIntegration = (
|
||||
req: GetPilotThingsIntegrationRequest,
|
||||
callbackFunc: (resp: GetPilotThingsIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getPilotThingsIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -539,10 +556,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updatePilotThingsIntegration = (req: UpdatePilotThingsIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updatePilotThingsIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updatePilotThingsIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -555,10 +572,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deletePilotThingsIntegration = (req: DeletePilotThingsIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deletePilotThingsIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deletePilotThingsIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -572,10 +589,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createLoraCloudIntegration = (req: CreateLoraCloudIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createLoraCloudIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createLoraCloudIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -588,9 +605,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getLoraCloudIntegration = (req: GetLoraCloudIntegrationRequest, callbackFunc: (resp: GetLoraCloudIntegrationResponse) => void) => {
|
||||
getLoraCloudIntegration = (
|
||||
req: GetLoraCloudIntegrationRequest,
|
||||
callbackFunc: (resp: GetLoraCloudIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getLoraCloudIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -599,10 +619,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateLoraCloudIntegration = (req: UpdateLoraCloudIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateLoraCloudIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateLoraCloudIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -615,10 +635,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteLoraCloudIntegration = (req: DeleteLoraCloudIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteLoraCloudIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteLoraCloudIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -632,10 +652,10 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createThingsBoardIntegration = (req: CreateThingsBoardIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.createThingsBoardIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createThingsBoardIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -648,9 +668,12 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getThingsBoardIntegration = (req: GetThingsBoardIntegrationRequest, callbackFunc: (resp: GetThingsBoardIntegrationResponse) => void) => {
|
||||
getThingsBoardIntegration = (
|
||||
req: GetThingsBoardIntegrationRequest,
|
||||
callbackFunc: (resp: GetThingsBoardIntegrationResponse) => void,
|
||||
) => {
|
||||
this.client.getThingsBoardIntegration(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -659,10 +682,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateThingsBoardIntegration = (req: UpdateThingsBoardIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.updateThingsBoardIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateThingsBoardIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -675,10 +698,10 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteThingsBoardIntegration = (req: DeleteThingsBoardIntegrationRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteThingsBoardIntegration(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteThingsBoardIntegration(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -692,9 +715,12 @@ class ApplicationStore extends EventEmitter {
|
||||
this.emit("integration.delete");
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
generateMqttIntegrationClientCertificate = (req: GenerateMqttIntegrationClientCertificateRequest, callbackFunc: (resp: GenerateMqttIntegrationClientCertificateResponse) => void) => {
|
||||
generateMqttIntegrationClientCertificate = (
|
||||
req: GenerateMqttIntegrationClientCertificateRequest,
|
||||
callbackFunc: (resp: GenerateMqttIntegrationClientCertificateResponse) => void,
|
||||
) => {
|
||||
this.client.generateMqttIntegrationClientCertificate(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -703,7 +729,7 @@ class ApplicationStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const applicationStore = new ApplicationStore();
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class DeviceProfileStore extends EventEmitter {
|
||||
client: DeviceProfileServiceClient;
|
||||
|
||||
@ -40,7 +39,7 @@ class DeviceProfileStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (req: GetDeviceProfileRequest, callbackFunc: (resp: GetDeviceProfileResponse) => void) => {
|
||||
this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -51,10 +50,10 @@ class DeviceProfileStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
update = (req: UpdateDeviceProfileRequest, callbackFunc: () => void) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -67,10 +66,10 @@ class DeviceProfileStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
delete = (req: DeleteDeviceProfileRequest, callbackFunc: () => void) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -83,7 +82,7 @@ class DeviceProfileStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
list = (req: ListDeviceProfilesRequest, callbackFunc: (resp: ListDeviceProfilesResponse) => void) => {
|
||||
this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -93,8 +92,8 @@ class DeviceProfileStore extends EventEmitter {
|
||||
}
|
||||
|
||||
callbackFunc(resp);
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
listAdrAlgorithms = (callbackFunc: (resp: ListDeviceProfileAdrAlgorithmsResponse) => void) => {
|
||||
this.client.listAdrAlgorithms(new google_protobuf_empty_pb.Empty(), SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -105,7 +104,7 @@ class DeviceProfileStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const deviceProfileStore = new DeviceProfileStore();
|
||||
|
@ -33,7 +33,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class DeviceStore extends EventEmitter {
|
||||
client: DeviceServiceClient;
|
||||
|
||||
@ -43,7 +42,7 @@ class DeviceStore extends EventEmitter {
|
||||
}
|
||||
|
||||
create = (req: CreateDeviceRequest, callbackFunc: () => void) => {
|
||||
this.client.create(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.create(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -56,7 +55,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (req: GetDeviceRequest, callbackFunc: (resp: GetDeviceResponse) => void) => {
|
||||
this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -67,10 +66,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
update = (req: UpdateDeviceRequest, callbackFunc: () => void) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -83,10 +82,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
delete = (req: DeleteDeviceRequest, callbackFunc: () => void) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -99,7 +98,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
list = (req: ListDevicesRequest, callbackFunc: (resp: ListDevicesResponse) => void) => {
|
||||
this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -110,10 +109,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createKeys = (req: CreateDeviceKeysRequest, callbackFunc: () => void) => {
|
||||
this.client.createKeys(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.createKeys(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -126,7 +125,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getKeys = (req: GetDeviceKeysRequest, callbackFunc: (resp?: GetDeviceKeysResponse) => void) => {
|
||||
this.client.getKeys(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -139,10 +138,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateKeys = (req: UpdateDeviceKeysRequest, callbackFunc: () => void) => {
|
||||
this.client.updateKeys(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateKeys(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -155,10 +154,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteKeys = (req: DeleteDeviceKeysRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteKeys(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteKeys(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -171,7 +170,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getStats = (req: GetDeviceStatsRequest, callbackFunc: (resp: GetDeviceStatsResponse) => void) => {
|
||||
this.client.getStats(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -182,7 +181,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
enqueue = (req: EnqueueDeviceQueueItemRequest, callbackFunc: (resp: EnqueueDeviceQueueItemResponse) => void) => {
|
||||
this.client.enqueue(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -193,10 +192,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
flushQueue = (req: FlushDeviceQueueRequest, callbackFunc: () => void) => {
|
||||
this.client.flushQueue(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.flushQueue(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -204,10 +203,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
flushDevNonces = (req: FlushDevNoncesRequest, callbackFunc: () => void) => {
|
||||
this.client.flushDevNonces(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.flushDevNonces(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -220,7 +219,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getQueue = (req: GetDeviceQueueItemsRequest, callbackFunc: (resp: GetDeviceQueueItemsResponse) => void) => {
|
||||
this.client.getQueue(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -231,10 +230,10 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
activate = (req: ActivateDeviceRequest, callbackFunc: () => void) => {
|
||||
this.client.activate(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.activate(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -247,7 +246,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getActivation = (req: GetDeviceActivationRequest, callbackFunc: (resp: GetDeviceActivationResponse) => void) => {
|
||||
this.client.getActivation(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -258,7 +257,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getRandomDevAddr = (req: GetRandomDevAddrRequest, callbackFunc: (resp: GetRandomDevAddrResponse) => void) => {
|
||||
this.client.getRandomDevAddr(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -269,7 +268,7 @@ class DeviceStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const deviceStore = new DeviceStore();
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class GatewayStore extends EventEmitter {
|
||||
client: GatewayServiceClient;
|
||||
|
||||
@ -28,7 +27,7 @@ class GatewayStore extends EventEmitter {
|
||||
}
|
||||
|
||||
create = (req: CreateGatewayRequest, callbackFunc: () => void) => {
|
||||
this.client.create(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.create(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -41,7 +40,7 @@ class GatewayStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (req: GetGatewayRequest, callbackFunc: (resp: GetGatewayResponse) => void) => {
|
||||
this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -52,10 +51,10 @@ class GatewayStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
update = (req: UpdateGatewayRequest, callbackFunc: () => void) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -68,10 +67,10 @@ class GatewayStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
delete = (req: DeleteGatewayRequest, callbackFunc: () => void) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -84,7 +83,7 @@ class GatewayStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
list = (req: ListGatewaysRequest, callbackFunc: (resp: ListGatewaysResponse) => void) => {
|
||||
this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -95,7 +94,7 @@ class GatewayStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getStats = (req: GetGatewayStatsRequest, callbackFunc: (resp: GetGatewayStatsResponse) => void) => {
|
||||
this.client.getStats(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -106,9 +105,12 @@ class GatewayStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
generateClientCertificate = (req: GenerateGatewayClientCertificateRequest, callbackFunc: (resp: GenerateGatewayClientCertificateResponse) => void) => {
|
||||
generateClientCertificate = (
|
||||
req: GenerateGatewayClientCertificateRequest,
|
||||
callbackFunc: (resp: GenerateGatewayClientCertificateResponse) => void,
|
||||
) => {
|
||||
this.client.generateClientCertificate(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
@ -117,7 +119,7 @@ class GatewayStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const gatewayStore = new GatewayStore();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import * as grpcWeb from 'grpc-web';
|
||||
import * as grpcWeb from "grpc-web";
|
||||
import google_protobuf_empty_pb from "google-protobuf/google/protobuf/empty_pb";
|
||||
|
||||
import { notification } from "antd";
|
||||
@ -26,7 +26,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class InternalStore extends EventEmitter {
|
||||
client: InternalServiceClient;
|
||||
|
||||
@ -49,7 +48,7 @@ class InternalStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteApiKey = (req: DeleteApiKeyRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteApiKey(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -65,7 +64,7 @@ class InternalStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
listApiKeys = (req: ListApiKeysRequest, callbackFunc: (resp: ListApiKeysResponse) => void) => {
|
||||
this.client.listApiKeys(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -76,26 +75,25 @@ class InternalStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
streamGatewayFrames = (req: StreamGatewayFramesRequest, callbackFunc: (resp: LogItem) => void): () => void => {
|
||||
streamGatewayFrames = (req: StreamGatewayFramesRequest, callbackFunc: (resp: LogItem) => void): (() => void) => {
|
||||
var stream: grpcWeb.ClientReadableStream<LogItem> | undefined = undefined;
|
||||
|
||||
let setup = () => {
|
||||
console.log("Setting up gRPC stream");
|
||||
stream = this.client.streamGatewayFrames(req, SessionStore.getMetadata());
|
||||
|
||||
stream = stream.on("data", (resp) => {
|
||||
stream = stream.on("data", resp => {
|
||||
callbackFunc(resp);
|
||||
});
|
||||
|
||||
stream = stream.on('end', function() {
|
||||
stream = stream.on("end", function () {
|
||||
console.log("gRPC stream end, reconnecting");
|
||||
setTimeout(setup, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
setup();
|
||||
|
||||
return () => {
|
||||
@ -103,26 +101,25 @@ class InternalStore extends EventEmitter {
|
||||
stream.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
streamDeviceFrames = (req: StreamDeviceFramesRequest, callbackFunc: (resp: LogItem) => void): () => void => {
|
||||
streamDeviceFrames = (req: StreamDeviceFramesRequest, callbackFunc: (resp: LogItem) => void): (() => void) => {
|
||||
var stream: grpcWeb.ClientReadableStream<LogItem> | undefined = undefined;
|
||||
|
||||
let setup = () => {
|
||||
console.log("Setting up gRPC stream");
|
||||
stream = this.client.streamDeviceFrames(req, SessionStore.getMetadata());
|
||||
|
||||
stream = stream.on("data", (resp) => {
|
||||
stream = stream.on("data", resp => {
|
||||
callbackFunc(resp);
|
||||
});
|
||||
|
||||
stream = stream.on('end', function() {
|
||||
stream = stream.on("end", function () {
|
||||
console.log("gRPC stream end, reconnecting");
|
||||
setTimeout(setup, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
setup();
|
||||
|
||||
return () => {
|
||||
@ -130,20 +127,20 @@ class InternalStore extends EventEmitter {
|
||||
stream.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
streamDeviceEvents = (req: StreamDeviceEventsRequest, callbackFunc: (resp: LogItem) => void): () => void => {
|
||||
streamDeviceEvents = (req: StreamDeviceEventsRequest, callbackFunc: (resp: LogItem) => void): (() => void) => {
|
||||
var stream: grpcWeb.ClientReadableStream<LogItem> | undefined = undefined;
|
||||
|
||||
let setup = () => {
|
||||
console.log("Setting up gRPC stream");
|
||||
stream = this.client.streamDeviceEvents(req, SessionStore.getMetadata());
|
||||
|
||||
stream = stream.on("data", (resp) => {
|
||||
stream = stream.on("data", resp => {
|
||||
callbackFunc(resp);
|
||||
});
|
||||
|
||||
stream = stream.on('end', function() {
|
||||
stream = stream.on("end", function () {
|
||||
console.log("gRPC stream end, reconnecting");
|
||||
setTimeout(setup, 1000);
|
||||
});
|
||||
@ -156,7 +153,7 @@ class InternalStore extends EventEmitter {
|
||||
stream.cancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
getGatewaysSummary = (req: GetGatewaysSummaryRequest, callbackFunc: (resp: GetGatewaysSummaryResponse) => void) => {
|
||||
this.client.getGatewaysSummary(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -167,7 +164,7 @@ class InternalStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getDevicesSummary = (req: GetDevicesSummaryRequest, callbackFunc: (resp: GetDevicesSummaryResponse) => void) => {
|
||||
this.client.getDevicesSummary(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -178,7 +175,7 @@ class InternalStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
settings = (callbackFunc: (resp: SettingsResponse) => void) => {
|
||||
this.client.settings(new google_protobuf_empty_pb.Empty(), {}, (err, resp) => {
|
||||
@ -189,7 +186,7 @@ class InternalStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
globalSearch = (req: GlobalSearchRequest, callbackFunc: (resp: GlobalSearchResponse) => void) => {
|
||||
this.client.globalSearch(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -200,7 +197,7 @@ class InternalStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const internalStore = new InternalStore();
|
||||
|
@ -11,17 +11,21 @@ class LocationStore extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition((p: GeolocationPosition) => {
|
||||
callbackFunc([p.coords.latitude, p.coords.longitude]);
|
||||
}, (e: GeolocationPositionError) => {
|
||||
notification.error({
|
||||
message: e.message,
|
||||
duration: 3,
|
||||
});
|
||||
}, {
|
||||
timeout: 3000,
|
||||
});
|
||||
}
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(p: GeolocationPosition) => {
|
||||
callbackFunc([p.coords.latitude, p.coords.longitude]);
|
||||
},
|
||||
(e: GeolocationPositionError) => {
|
||||
notification.error({
|
||||
message: e.message,
|
||||
duration: 3,
|
||||
});
|
||||
},
|
||||
{
|
||||
timeout: 3000,
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const locationStore = new LocationStore();
|
||||
|
@ -20,7 +20,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class MulticastGroupStore extends EventEmitter {
|
||||
client: MulticastGroupServiceClient;
|
||||
|
||||
@ -43,7 +42,7 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (req: GetMulticastGroupRequest, callbackFunc: (resp: GetMulticastGroupResponse) => void) => {
|
||||
this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -54,10 +53,10 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
update = (req: UpdateMulticastGroupRequest, callbackFunc: () => void) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -70,10 +69,10 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
delete = (req: DeleteMulticastGroupRequest, callbackFunc: () => void) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -86,7 +85,7 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
list = (req: ListMulticastGroupsRequest, callbackFunc: (resp: ListMulticastGroupsResponse) => void) => {
|
||||
this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -97,10 +96,10 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
addDevice = (req: AddDeviceToMulticastGroupRequest, callbackFunc: () => void) => {
|
||||
this.client.addDevice(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.addDevice(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -113,10 +112,10 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
removeDevice = (req: RemoveDeviceFromMulticastGroupRequest, callbackFunc: () => void) => {
|
||||
this.client.removeDevice(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.removeDevice(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -124,7 +123,7 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
listQueue = (req: ListMulticastGroupQueueRequest, callbackFunc: (resp: ListMulticastGroupQueueResponse) => void) => {
|
||||
this.client.listQueue(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -135,7 +134,7 @@ class MulticastGroupStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const multicastGroupStore = new MulticastGroupStore();
|
||||
|
@ -3,7 +3,11 @@ import { Metadata } from "grpc-web";
|
||||
|
||||
import { EventEmitter } from "events";
|
||||
import { InternalServiceClient } from "@chirpstack/chirpstack-api-grpc-web/api/internal_grpc_web_pb";
|
||||
import { LoginRequest, UserTenantLink, OpenIdConnectLoginRequest } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
import {
|
||||
LoginRequest,
|
||||
UserTenantLink,
|
||||
OpenIdConnectLoginRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
import { User } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
|
||||
|
||||
import { HandleError, HandleLoginError } from "./helpers";
|
||||
@ -35,7 +39,7 @@ class SessionStore extends EventEmitter {
|
||||
this.setToken(resp.getJwt());
|
||||
this.fetchProfile(callbackFunc);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
openIdConnectLogin = (req: OpenIdConnectLoginRequest, callbackFunc: any) => {
|
||||
this.client.openIdConnectLogin(req, {}, (err, resp) => {
|
||||
@ -47,7 +51,7 @@ class SessionStore extends EventEmitter {
|
||||
this.setToken(resp.getToken());
|
||||
this.fetchProfile(callbackFunc);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
logout = (emit: boolean, callbackFunc: () => void) => {
|
||||
localStorage.clear();
|
||||
@ -59,11 +63,11 @@ class SessionStore extends EventEmitter {
|
||||
}
|
||||
|
||||
callbackFunc();
|
||||
}
|
||||
};
|
||||
|
||||
setToken = (s: string) => {
|
||||
localStorage.setItem("token", s);
|
||||
}
|
||||
};
|
||||
|
||||
getToken = (): string => {
|
||||
let token = localStorage.getItem("token");
|
||||
@ -71,25 +75,25 @@ class SessionStore extends EventEmitter {
|
||||
return "";
|
||||
}
|
||||
return token;
|
||||
}
|
||||
};
|
||||
|
||||
getTenantId = (): string => {
|
||||
return localStorage.getItem("tenantId") || "";
|
||||
}
|
||||
};
|
||||
|
||||
setTenantId = (id: string) => {
|
||||
console.log("tenantId set to", id);
|
||||
localStorage.setItem("tenantId", id);
|
||||
this.emit("tenant.change");
|
||||
}
|
||||
};
|
||||
|
||||
getRowsPerPage = (): number => {
|
||||
return parseInt(localStorage.getItem("rowsPerPage") || "10", 10);
|
||||
}
|
||||
};
|
||||
|
||||
setRowsPerPage = (count: number) => {
|
||||
localStorage.setItem("rowsPerPage", count.toString());
|
||||
}
|
||||
};
|
||||
|
||||
getMetadata = (): Metadata => {
|
||||
if (this.getToken() === "") {
|
||||
@ -99,7 +103,7 @@ class SessionStore extends EventEmitter {
|
||||
return {
|
||||
authorization: "Bearer " + this.getToken(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
fetchProfile = (callbackFunc: any) => {
|
||||
if (this.getToken() === "") {
|
||||
@ -118,19 +122,19 @@ class SessionStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getUser = (): User | undefined => {
|
||||
return this.user;
|
||||
}
|
||||
};
|
||||
|
||||
isAdmin = (): boolean => {
|
||||
if(!this.user) {
|
||||
if (!this.user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.user.getIsAdmin();
|
||||
}
|
||||
};
|
||||
|
||||
isTenantAdmin = (tenantId: string): boolean => {
|
||||
for (const t of this.tenants) {
|
||||
@ -140,7 +144,7 @@ class SessionStore extends EventEmitter {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
isTenantDeviceAdmin = (tenantId: string): boolean => {
|
||||
for (const t of this.tenants) {
|
||||
@ -150,7 +154,7 @@ class SessionStore extends EventEmitter {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
isTenantGatewayAdmin = (tenantId: string): boolean => {
|
||||
for (const t of this.tenants) {
|
||||
@ -158,7 +162,7 @@ class SessionStore extends EventEmitter {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const sessionStore = new SessionStore();
|
||||
|
@ -22,7 +22,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class TenantStore extends EventEmitter {
|
||||
client: TenantServiceClient;
|
||||
|
||||
@ -45,7 +44,7 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (id: string, callbackFunc: (resp: GetTenantResponse) => void) => {
|
||||
let req = new GetTenantRequest();
|
||||
@ -59,7 +58,7 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
update = (req: UpdateTenantRequest, callbackFunc: () => void) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -77,7 +76,7 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
delete = (req: DeleteTenantRequest, callbackFunc: () => void) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -93,7 +92,7 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
list = (req: ListTenantsRequest, callbackFunc: (resp: ListTenantsResponse) => void) => {
|
||||
this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -104,10 +103,10 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
addUser = (req: AddTenantUserRequest, callbackFunc: () => void) => {
|
||||
this.client.addUser(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.addUser(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -131,10 +130,10 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateUser = (req: UpdateTenantUserRequest, callbackFunc: () => void) => {
|
||||
this.client.updateUser(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updateUser(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -147,10 +146,10 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteUser = (req: DeleteTenantUserRequest, callbackFunc: () => void) => {
|
||||
this.client.deleteUser(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.deleteUser(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -163,7 +162,7 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
listUsers = (req: ListTenantUsersRequest, callbackFunc: (resp: ListTenantUsersResponse) => void) => {
|
||||
this.client.listUsers(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -174,7 +173,7 @@ class TenantStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const tenantStore = new TenantStore();
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
import SessionStore from "./SessionStore";
|
||||
import { HandleError } from "./helpers";
|
||||
|
||||
|
||||
class UserStore extends EventEmitter {
|
||||
client: UserServiceClient;
|
||||
|
||||
@ -39,7 +38,7 @@ class UserStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (req: GetUserRequest, callbackFunc: (resp: GetUserResponse) => void) => {
|
||||
this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -50,7 +49,7 @@ class UserStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
update = (req: UpdateUserRequest, callbackFunc: () => void) => {
|
||||
this.client.update(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -66,7 +65,7 @@ class UserStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
delete = (req: DeleteUserRequest, callbackFunc: () => void) => {
|
||||
this.client.delete(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -82,7 +81,7 @@ class UserStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
list = (req: ListUsersRequest, callbackFunc: (resp: ListUsersResponse) => void) => {
|
||||
this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
|
||||
@ -93,10 +92,10 @@ class UserStore extends EventEmitter {
|
||||
|
||||
callbackFunc(resp);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updatePassword = (req: UpdateUserPasswordRequest, callbackFunc: () => void) => {
|
||||
this.client.updatePassword(req, SessionStore.getMetadata(), (err) => {
|
||||
this.client.updatePassword(req, SessionStore.getMetadata(), err => {
|
||||
if (err !== null) {
|
||||
HandleError(err);
|
||||
return;
|
||||
@ -109,7 +108,7 @@ class UserStore extends EventEmitter {
|
||||
|
||||
callbackFunc();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const userStore = new UserStore();
|
||||
|
@ -6,7 +6,7 @@ import history from "../history";
|
||||
export function HandleError(e: Error) {
|
||||
console.log("API error: ", e);
|
||||
|
||||
if (e.code === 16 || e.code === 2) {
|
||||
if (e.code === 16 || e.code === 2) {
|
||||
history.push("/login");
|
||||
return;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import { Form, Input, Button } from "antd";
|
||||
|
||||
import { ApiKey } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
|
||||
|
||||
interface IProps {
|
||||
initialValues: ApiKey;
|
||||
onFinish: (obj: ApiKey) => void;
|
||||
@ -17,20 +16,18 @@ class ApiKeyForm extends Component<IProps, IState> {
|
||||
let apiKey = new ApiKey();
|
||||
apiKey.setName(values.name);
|
||||
this.props.onFinish(apiKey);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.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 />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,31 +1,28 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Input, Typography, Button, Space } from 'antd';
|
||||
import { Input, Typography, Button, Space } from "antd";
|
||||
|
||||
import { CreateApiKeyResponse } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
|
||||
|
||||
interface IProps {
|
||||
createApiKeyResponse: CreateApiKeyResponse;
|
||||
}
|
||||
|
||||
|
||||
class ApiKeyToken extends Component<IProps> {
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
<Typography>
|
||||
<Typography.Paragraph>
|
||||
Use the following API token when making API requests. This token can
|
||||
be revoked at any time by deleting it. Please note that this token can
|
||||
only be retrieved once:
|
||||
Use the following API token when making API requests. This token can be revoked at any time by deleting it.
|
||||
Please note that this token can only be retrieved once:
|
||||
</Typography.Paragraph>
|
||||
</Typography>
|
||||
<Input.TextArea rows={4} value={this.props.createApiKeyResponse.getToken()} />
|
||||
<Button type="primary"><Link to="../api-keys">Back</Link></Button>
|
||||
<Button type="primary">
|
||||
<Link to="../api-keys">Back</Link>
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -9,14 +9,12 @@ import ApiKeyForm from "./ApiKeyForm";
|
||||
import ApiKeyToken from "./ApiKeyToken";
|
||||
import InternalStore from "../../stores/InternalStore";
|
||||
|
||||
|
||||
interface IProps {}
|
||||
|
||||
interface IState {
|
||||
createApiKeyResponse?: CreateApiKeyResponse;
|
||||
}
|
||||
|
||||
|
||||
class CreateAdminApiKey extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -34,26 +32,30 @@ class CreateAdminApiKey extends Component<IProps, IState> {
|
||||
createApiKeyResponse: resp,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const apiKey = new ApiKey();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
title="Add API key"
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Network-server</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to="/api-keys">API keys</Link></span>
|
||||
<span>
|
||||
<Link to="/api-keys">API keys</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Add</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
/>
|
||||
<Card>
|
||||
{!this.state.createApiKeyResponse && <ApiKeyForm initialValues={apiKey} onFinish={this.onFinish} />}
|
||||
|
@ -10,7 +10,6 @@ import ApiKeyForm from "./ApiKeyForm";
|
||||
import ApiKeyToken from "./ApiKeyToken";
|
||||
import InternalStore from "../../stores/InternalStore";
|
||||
|
||||
|
||||
interface IState {
|
||||
createApiKeyResponse?: CreateApiKeyResponse;
|
||||
}
|
||||
@ -19,7 +18,6 @@ interface IProps {
|
||||
tenant: Tenant;
|
||||
}
|
||||
|
||||
|
||||
class CreateTenantApiKey extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -37,28 +35,34 @@ class CreateTenantApiKey extends Component<IProps, IState> {
|
||||
createApiKeyResponse: resp,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const apiKey = new ApiKey();
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/api-keys`}>API Keys</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/api-keys`}>API Keys</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Add</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="Add API key"
|
||||
/>
|
||||
<Card>
|
||||
|
@ -6,20 +6,23 @@ import { DeleteOutlined } from "@ant-design/icons";
|
||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
||||
import { ColumnsType } from "antd/es/table";
|
||||
|
||||
import { ListApiKeysRequest, ListApiKeysResponse, DeleteApiKeyRequest, ApiKey } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
import {
|
||||
ListApiKeysRequest,
|
||||
ListApiKeysResponse,
|
||||
DeleteApiKeyRequest,
|
||||
ApiKey,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
|
||||
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
||||
import InternalStore from "../../stores/InternalStore";
|
||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||
|
||||
|
||||
interface IProps {}
|
||||
|
||||
interface IState {
|
||||
refreshKey: number;
|
||||
}
|
||||
|
||||
|
||||
class ListAdminApiKeys extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -47,19 +50,15 @@ class ListAdminApiKeys extends Component<IProps, IState> {
|
||||
key: "action",
|
||||
width: 100,
|
||||
render: (text, record) => (
|
||||
<DeleteConfirm
|
||||
typ="API key"
|
||||
confirm={record.name}
|
||||
onConfirm={this.deleteApiKey(record.id)}
|
||||
>
|
||||
<DeleteConfirm typ="API key" confirm={record.name} onConfirm={this.deleteApiKey(record.id)}>
|
||||
<Button shape="circle" icon={<DeleteOutlined />} />
|
||||
</DeleteConfirm>
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
deleteApiKey = (id: string): () => void => {
|
||||
deleteApiKey = (id: string): (() => void) => {
|
||||
return () => {
|
||||
let req = new DeleteApiKeyRequest();
|
||||
req.setId(id);
|
||||
@ -70,8 +69,8 @@ class ListAdminApiKeys extends Component<IProps, IState> {
|
||||
refreshKey: this.state.refreshKey + 1,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||
let req = new ListApiKeysRequest();
|
||||
@ -83,31 +82,30 @@ class ListAdminApiKeys extends Component<IProps, IState> {
|
||||
const obj = resp.toObject();
|
||||
callbackFunc(obj.totalCount, obj.resultList);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Network-server</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>API keys</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="API keys"
|
||||
extra={[
|
||||
<Button type="primary"><Link to="/api-keys/create">Add API key</Link></Button>
|
||||
<Button type="primary">
|
||||
<Link to="/api-keys/create">Add API key</Link>
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
<DataTable
|
||||
columns={this.columns()}
|
||||
getPage={this.getPage}
|
||||
rowKey="id"
|
||||
refreshKey={this.state.refreshKey}
|
||||
/>
|
||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" refreshKey={this.state.refreshKey} />
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -5,7 +5,12 @@ import { DeleteOutlined } from "@ant-design/icons";
|
||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
||||
import { ColumnsType } from "antd/es/table";
|
||||
|
||||
import { ListApiKeysRequest, ListApiKeysResponse, DeleteApiKeyRequest, ApiKey } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
import {
|
||||
ListApiKeysRequest,
|
||||
ListApiKeysResponse,
|
||||
DeleteApiKeyRequest,
|
||||
ApiKey,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
|
||||
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
||||
@ -13,7 +18,6 @@ import InternalStore from "../../stores/InternalStore";
|
||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||
import Admin from "../../components/Admin";
|
||||
|
||||
|
||||
interface IProps {
|
||||
tenant: Tenant;
|
||||
isAdmin: boolean;
|
||||
@ -23,13 +27,12 @@ interface IState {
|
||||
refreshKey: number;
|
||||
}
|
||||
|
||||
|
||||
class ListTenantApiKeys extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
refreshKey: 1,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
columns = (): ColumnsType<ApiKey.AsObject> => {
|
||||
@ -52,20 +55,16 @@ class ListTenantApiKeys extends Component<IProps, IState> {
|
||||
width: 100,
|
||||
render: (text, record) => (
|
||||
<Admin tenantId={this.props.tenant.getId()} isTenantAdmin>
|
||||
<DeleteConfirm
|
||||
typ="API key"
|
||||
confirm={record.name}
|
||||
onConfirm={this.deleteApiKey(record.id)}
|
||||
>
|
||||
<DeleteConfirm typ="API key" confirm={record.name} onConfirm={this.deleteApiKey(record.id)}>
|
||||
<Button shape="circle" icon={<DeleteOutlined />} />
|
||||
</DeleteConfirm>
|
||||
</Admin>
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
deleteApiKey = (id: string): () => void => {
|
||||
deleteApiKey = (id: string): (() => void) => {
|
||||
return () => {
|
||||
let req = new DeleteApiKeyRequest();
|
||||
req.setId(id);
|
||||
@ -76,8 +75,8 @@ class ListTenantApiKeys extends Component<IProps, IState> {
|
||||
refreshKey: this.state.refreshKey + 1,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||
let req = new ListApiKeysRequest();
|
||||
@ -89,36 +88,37 @@ class ListTenantApiKeys extends Component<IProps, IState> {
|
||||
const obj = resp.toObject();
|
||||
callbackFunc(obj.totalCount, obj.resultList);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>API Keys</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="API keys"
|
||||
extra={[
|
||||
<Admin tenantId={this.props.tenant.getId()} isTenantAdmin>
|
||||
<Button type="primary"><Link to={`/tenants/${this.props.tenant.getId()}/api-keys/create`}>Add API key</Link></Button>
|
||||
</Admin>
|
||||
<Button type="primary">
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/api-keys/create`}>Add API key</Link>
|
||||
</Button>
|
||||
</Admin>,
|
||||
]}
|
||||
/>
|
||||
<DataTable
|
||||
columns={this.columns()}
|
||||
getPage={this.getPage}
|
||||
rowKey="id"
|
||||
refreshKey={this.state.refreshKey}
|
||||
/>
|
||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" refreshKey={this.state.refreshKey} />
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import React, { Component } from "react";
|
||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Form, Input, Button } from "antd";
|
||||
|
||||
|
||||
interface IProps {
|
||||
initialValues: Application;
|
||||
onFinish: (obj: Application) => void;
|
||||
@ -21,26 +20,21 @@ class ApplicationForm extends Component<IProps> {
|
||||
app.setDescription(v.description);
|
||||
|
||||
this.props.onFinish(app);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.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} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Description"
|
||||
name="description"
|
||||
>
|
||||
<Form.Item label="Description" name="description">
|
||||
<Input.TextArea disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" disabled={this.props.disabled}>Submit</Button>
|
||||
<Button type="primary" htmlType="submit" disabled={this.props.disabled}>
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -35,13 +35,11 @@ import CreateThingsBoardIntegration from "./integrations/CreateThingsBoardIntegr
|
||||
import EditThingsBoardIntegration from "./integrations/EditThingsBoardIntegration";
|
||||
import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
tenant: Tenant;
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class ApplicationLayout extends Component<IProps> {
|
||||
deleteApplication = () => {
|
||||
let req = new DeleteApplicationRequest();
|
||||
@ -50,7 +48,7 @@ class ApplicationLayout extends Component<IProps> {
|
||||
ApplicationStore.delete(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const tenant = this.props.tenant;
|
||||
@ -73,73 +71,180 @@ class ApplicationLayout extends Component<IProps> {
|
||||
tab = "integrations";
|
||||
}
|
||||
|
||||
const showIntegrations = SessionStore.isAdmin() || SessionStore.isTenantAdmin(tenant.getId()) || SessionStore.isTenantDeviceAdmin(tenant.getId());
|
||||
const showIntegrations =
|
||||
SessionStore.isAdmin() ||
|
||||
SessionStore.isTenantAdmin(tenant.getId()) ||
|
||||
SessionStore.isTenantDeviceAdmin(tenant.getId());
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>{app.getName()}</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title={app.getName()}
|
||||
subTitle={`application id: ${app.getId()}`}
|
||||
extra={[
|
||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
||||
<DeleteConfirm
|
||||
confirm={app.getName()}
|
||||
typ="application"
|
||||
onConfirm={this.deleteApplication}
|
||||
>
|
||||
<Button danger type="primary">Delete application</Button>
|
||||
<DeleteConfirm confirm={app.getName()} typ="application" onConfirm={this.deleteApplication}>
|
||||
<Button danger type="primary">
|
||||
Delete application
|
||||
</Button>
|
||||
</DeleteConfirm>
|
||||
</Admin>
|
||||
</Admin>,
|
||||
]}
|
||||
/>
|
||||
<Card>
|
||||
<Menu mode="horizontal" selectedKeys={[tab]} style={{marginBottom: 24}}>
|
||||
<Menu.Item key="devices"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}`}>Devices</Link></Menu.Item>
|
||||
<Menu.Item key="mg"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/multicast-groups`}>Multicast groups</Link></Menu.Item>
|
||||
<Menu.Item key="edit"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/edit`}>Application configuration</Link></Menu.Item>
|
||||
{showIntegrations && <Menu.Item key="integrations"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/integrations`}>Integrations</Link></Menu.Item> }
|
||||
</Menu>
|
||||
<Switch>
|
||||
<Route exact path={this.props.match.path} render={props => <ListDevices application={app} {...props} />} />
|
||||
<Route exact path={`${this.props.match.path}/edit`} render={props => <EditApplication application={app} {...props} />} />
|
||||
<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} />} />
|
||||
<Card>
|
||||
<Menu mode="horizontal" selectedKeys={[tab]} style={{ marginBottom: 24 }}>
|
||||
<Menu.Item key="devices">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}`}>Devices</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="mg">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/multicast-groups`}>
|
||||
Multicast groups
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="edit">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/edit`}>Application configuration</Link>
|
||||
</Menu.Item>
|
||||
{showIntegrations && (
|
||||
<Menu.Item key="integrations">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/integrations`}>Integrations</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
<Switch>
|
||||
<Route exact path={this.props.match.path} render={props => <ListDevices application={app} {...props} />} />
|
||||
<Route
|
||||
exact
|
||||
path={`${this.props.match.path}/edit`}
|
||||
render={props => <EditApplication application={app} {...props} />}
|
||||
/>
|
||||
<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}/integrations/http/create`} render={props => <CreateHttpIntegration application={app} {...props} />} />
|
||||
<Route exact path={`${this.props.match.path}/integrations/http/edit`} render={props => <EditHttpIntegration application={app} {...props} />} />
|
||||
<Route exact path={`${this.props.match.path}/integrations/aws-sns/create`} render={props => <CreateAwsSnsIntegration application={app} {...props} />} />
|
||||
<Route 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} />} />
|
||||
</Switch>
|
||||
</Card>
|
||||
<Route
|
||||
exact
|
||||
path={`${this.props.match.path}/integrations/http/create`}
|
||||
render={props => <CreateHttpIntegration application={app} {...props} />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${this.props.match.path}/integrations/http/edit`}
|
||||
render={props => <EditHttpIntegration application={app} {...props} />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${this.props.match.path}/integrations/aws-sns/create`}
|
||||
render={props => <CreateAwsSnsIntegration application={app} {...props} />}
|
||||
/>
|
||||
<Route
|
||||
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} />}
|
||||
/>
|
||||
</Switch>
|
||||
</Card>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,11 @@ import React, { Component } from "react";
|
||||
import { Route, Switch, RouteComponentProps } from "react-router-dom";
|
||||
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import { Application, GetApplicationRequest, GetApplicationResponse } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import {
|
||||
Application,
|
||||
GetApplicationRequest,
|
||||
GetApplicationResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../stores/ApplicationStore";
|
||||
import ApplicationLayout from "./ApplicationLayout";
|
||||
@ -11,7 +15,6 @@ import DeviceLayout from "../devices/DeviceLayout";
|
||||
import MulticastGroupLayout from "../multicast-groups/MulticastGroupLayout";
|
||||
import CreateMulticastGroup from "../multicast-groups/CreateMulticastGroup";
|
||||
|
||||
|
||||
interface MatchParams {
|
||||
applicationId: string;
|
||||
}
|
||||
@ -24,7 +27,6 @@ interface IState {
|
||||
application?: Application;
|
||||
}
|
||||
|
||||
|
||||
class ApplicationLoader extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -44,7 +46,7 @@ class ApplicationLoader extends Component<IProps, IState> {
|
||||
application: resp.getApplication(),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const app = this.state.application;
|
||||
@ -55,13 +57,26 @@ class ApplicationLoader extends Component<IProps, IState> {
|
||||
const path = this.props.match.path;
|
||||
const tenant = this.props.tenant;
|
||||
|
||||
|
||||
return(
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path={`${path}/devices/create`} render={props => <CreateDevice tenant={tenant} application={app} {...props} />} />
|
||||
<Route exact path={`${path}/multicast-groups/create`} render={props => <CreateMulticastGroup tenant={tenant} application={app} {...props} />} />
|
||||
<Route 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
|
||||
exact
|
||||
path={`${path}/devices/create`}
|
||||
render={props => <CreateDevice tenant={tenant} application={app} {...props} />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${path}/multicast-groups/create`}
|
||||
render={props => <CreateMulticastGroup tenant={tenant} application={app} {...props} />}
|
||||
/>
|
||||
<Route
|
||||
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} render={props => <ApplicationLayout tenant={tenant} application={app} {...props} />} />
|
||||
</Switch>
|
||||
);
|
||||
|
@ -4,17 +4,19 @@ import { Link, RouteComponentProps } from "react-router-dom";
|
||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
||||
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import { Application, CreateApplicationRequest, CreateApplicationResponse } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import {
|
||||
Application,
|
||||
CreateApplicationRequest,
|
||||
CreateApplicationResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationForm from "./ApplicationForm";
|
||||
import ApplicationStore from "../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
tenant: Tenant;
|
||||
}
|
||||
|
||||
|
||||
class CreateApplication extends Component<IProps> {
|
||||
onFinish = (obj: Application) => {
|
||||
obj.setTenantId(this.props.tenant.getId());
|
||||
@ -25,28 +27,34 @@ class CreateApplication extends Component<IProps> {
|
||||
ApplicationStore.create(req, (resp: CreateApplicationResponse) => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${resp.getId()}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const app = new Application();
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Add</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="Add application"
|
||||
/>
|
||||
<Card>
|
||||
|
@ -7,28 +7,30 @@ import ApplicationStore from "../../stores/ApplicationStore";
|
||||
import ApplicationForm from "./ApplicationForm";
|
||||
import SessionStore from "../../stores/SessionStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class EditApplication extends Component<IProps> {
|
||||
onFinish = (obj: Application) => {
|
||||
let req = new UpdateApplicationRequest();
|
||||
req.setApplication(obj);
|
||||
|
||||
ApplicationStore.update(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const disabled = !(SessionStore.isAdmin() || SessionStore.isTenantAdmin(this.props.application.getTenantId()) || SessionStore.isTenantDeviceAdmin(this.props.application.getTenantId()));
|
||||
|
||||
return(
|
||||
<ApplicationForm initialValues={this.props.application} disabled={disabled} onFinish={this.onFinish} />
|
||||
const disabled = !(
|
||||
SessionStore.isAdmin() ||
|
||||
SessionStore.isTenantAdmin(this.props.application.getTenantId()) ||
|
||||
SessionStore.isTenantDeviceAdmin(this.props.application.getTenantId())
|
||||
);
|
||||
|
||||
return <ApplicationForm initialValues={this.props.application} disabled={disabled} onFinish={this.onFinish} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,19 +4,21 @@ import { Link } from "react-router-dom";
|
||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
||||
import { ColumnsType } from "antd/es/table";
|
||||
|
||||
import { ListApplicationsRequest, ListApplicationsResponse, ApplicationListItem } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import {
|
||||
ListApplicationsRequest,
|
||||
ListApplicationsResponse,
|
||||
ApplicationListItem,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
|
||||
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
||||
import ApplicationStore from "../../stores/ApplicationStore";
|
||||
import Admin from "../../components/Admin";
|
||||
|
||||
|
||||
interface IProps {
|
||||
tenant: Tenant;
|
||||
}
|
||||
|
||||
|
||||
class ListApplications extends Component<IProps> {
|
||||
columns = (): ColumnsType<ApplicationListItem.AsObject> => {
|
||||
return [
|
||||
@ -35,7 +37,7 @@ class ListApplications extends Component<IProps> {
|
||||
key: "description",
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||
let req = new ListApplicationsRequest();
|
||||
@ -47,35 +49,37 @@ class ListApplications extends Component<IProps> {
|
||||
const obj = resp.toObject();
|
||||
callbackFunc(obj.totalCount, obj.resultList);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Applications</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="Applications"
|
||||
extra={[
|
||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
||||
<Button type="primary"><Link to={`/tenants/${this.props.tenant.getId()}/applications/create`}>Add application</Link></Button>
|
||||
</Admin>
|
||||
<Button type="primary">
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/create`}>Add application</Link>
|
||||
</Button>
|
||||
</Admin>,
|
||||
]}
|
||||
/>
|
||||
<DataTable
|
||||
columns={this.columns()}
|
||||
getPage={this.getPage}
|
||||
rowKey="id"
|
||||
/>
|
||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" />
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import PilotThingsCard from "./integrations/PilotThingsCard";
|
||||
import LoRaCloudCard from "./integrations/LoRaCloudCard";
|
||||
import ThingsBoardCard from "./integrations/ThingsBoardCard";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application;
|
||||
}
|
||||
@ -32,7 +31,6 @@ interface IState {
|
||||
available: any[];
|
||||
}
|
||||
|
||||
|
||||
class ListIntegrations extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -70,83 +68,82 @@ class ListIntegrations extends Component<IProps, IState> {
|
||||
};
|
||||
|
||||
// AWS SNS
|
||||
if(includes(resp.getResultList(), IntegrationKind.AWS_SNS)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.AWS_SNS)) {
|
||||
configured.push(<AwsSnsCard application={this.props.application} />);
|
||||
} else {
|
||||
available.push(<AwsSnsCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// 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} />);
|
||||
} else {
|
||||
available.push(<AzureServiceBusCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// 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} />);
|
||||
} else {
|
||||
available.push(<GcpPubSubCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// HTTP
|
||||
if(includes(resp.getResultList(), IntegrationKind.HTTP)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.HTTP)) {
|
||||
configured.push(<HttpCard application={this.props.application} />);
|
||||
} else {
|
||||
available.push(<HttpCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// InfluxDB
|
||||
if(includes(resp.getResultList(), IntegrationKind.INFLUX_DB)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.INFLUX_DB)) {
|
||||
configured.push(<InfluxdbCard application={this.props.application} />);
|
||||
} else {
|
||||
available.push(<InfluxdbCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// MQTT
|
||||
if(includes(resp.getResultList(), IntegrationKind.MQTT_GLOBAL)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.MQTT_GLOBAL)) {
|
||||
configured.push(<MqttCard application={this.props.application} />);
|
||||
}
|
||||
|
||||
// myDevices
|
||||
if(includes(resp.getResultList(), IntegrationKind.MY_DEVICES)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.MY_DEVICES)) {
|
||||
configured.push(<MyDevicesCard application={this.props.application} />);
|
||||
} else {
|
||||
available.push(<MyDevicesCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// Pilot Things
|
||||
if(includes(resp.getResultList(), IntegrationKind.PILOT_THINGS)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.PILOT_THINGS)) {
|
||||
configured.push(<PilotThingsCard application={this.props.application} />);
|
||||
} else {
|
||||
available.push(<PilotThingsCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// Semtech LoRa Cloud
|
||||
if(includes(resp.getResultList(), IntegrationKind.LORA_CLOUD)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.LORA_CLOUD)) {
|
||||
configured.push(<LoRaCloudCard application={this.props.application} />);
|
||||
} else {
|
||||
available.push(<LoRaCloudCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
// ThingsBoard
|
||||
if(includes(resp.getResultList(), IntegrationKind.THINGS_BOARD)) {
|
||||
if (includes(resp.getResultList(), IntegrationKind.THINGS_BOARD)) {
|
||||
configured.push(<ThingsBoardCard application={this.props.application} />);
|
||||
} else {
|
||||
available.push(<ThingsBoardCard application={this.props.application} add />);
|
||||
}
|
||||
|
||||
|
||||
this.setState({
|
||||
configured: configured,
|
||||
available: available,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Row gutter={24}>
|
||||
{this.state.configured}
|
||||
{this.state.available}
|
||||
|
@ -1,45 +1,41 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
Application,
|
||||
DeleteAwsSnsIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Application, DeleteAwsSnsIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class AwsSns extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteAwsSnsIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
|
||||
ApplicationStore.deleteAwsSnsIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/aws-sns/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/aws-sns/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/aws-sns/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/aws-sns/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -50,12 +46,10 @@ class AwsSns extends Component<IProps> {
|
||||
<Card
|
||||
title="AWS SNS"
|
||||
className="integration-card"
|
||||
cover={<img alt="AWS SNS" src="/integrations/aws_sns.png" style={{padding: 1}} />}
|
||||
cover={<img alt="AWS SNS" src="/integrations/aws_sns.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The AWS SNS integration forwards events to an AWS SNS topic."
|
||||
/>
|
||||
<Card.Meta description="The AWS SNS integration forwards events to an AWS SNS topic." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -2,11 +2,7 @@ import React, { Component } from "react";
|
||||
|
||||
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";
|
||||
|
||||
interface IProps {
|
||||
initialValues: AwsSnsIntegration;
|
||||
@ -26,51 +22,49 @@ class AwsSnsIntegrationForm extends Component<IProps> {
|
||||
i.setTopicArn(v.topicArn);
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="Payload encoding"
|
||||
name="encoding"
|
||||
rules={[{required: true, message: "Please select an encoding!"}]}
|
||||
rules={[{ required: true, message: "Please select an encoding!" }]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value={Encoding.JSON}>JSON</Select.Option>
|
||||
<Select.Option value={Encoding.PROTOBUF}>Protobuf (binary)</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="AWS region"
|
||||
name="region"
|
||||
rules={[{required: true, message: "Please enter a region!"}]}
|
||||
>
|
||||
<Form.Item label="AWS region" name="region" rules={[{ required: true, message: "Please enter a region!" }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="AWS Access Key ID"
|
||||
name="accessKeyId"
|
||||
rules={[{required: true, message: "Please enter an Access Key ID!"}]}
|
||||
rules={[{ required: true, message: "Please enter an Access Key ID!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="AWS Secret Access Key"
|
||||
name="secretAccessKey"
|
||||
rules={[{required: true, message: "Please enter a Secret Access Key!"}]}
|
||||
rules={[{ required: true, message: "Please enter a Secret Access Key!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="AWS SNS topic ARN"
|
||||
name="topicArn"
|
||||
rules={[{required: true, message: "Please enter a SNS topic ARN!"}]}
|
||||
rules={[{ required: true, message: "Please enter a SNS topic ARN!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
@ -11,34 +11,33 @@ import {
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class AzureServiceBusCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteAzureServiceBusIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deleteAzureServiceBusIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/azure-service-bus/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/azure-service-bus/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/azure-service-bus/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/azure-service-bus/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +48,10 @@ class AzureServiceBusCard extends Component<IProps> {
|
||||
<Card
|
||||
title="Azure Service-Bus"
|
||||
className="integration-card"
|
||||
cover={<img alt="Azure Service-Bus" src="/integrations/azure_service_bus.png" style={{padding: 1}} />}
|
||||
cover={<img alt="Azure Service-Bus" src="/integrations/azure_service_bus.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The Azure Service-Bus integration forwards events to an Azure Service-Bus topic or queue."
|
||||
/>
|
||||
<Card.Meta description="The Azure Service-Bus integration forwards events to an Azure Service-Bus topic or queue." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -2,18 +2,13 @@ import React, { Component } from "react";
|
||||
|
||||
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";
|
||||
|
||||
interface IProps {
|
||||
initialValues: AzureServiceBusIntegration;
|
||||
onFinish: (obj: AzureServiceBusIntegration) => void;
|
||||
}
|
||||
|
||||
|
||||
class AzureServiceBusIntegrationForm extends Component<IProps> {
|
||||
onFinish = (values: AzureServiceBusIntegration.AsObject) => {
|
||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
||||
@ -25,15 +20,15 @@ class AzureServiceBusIntegrationForm extends Component<IProps> {
|
||||
i.setPublishName(v.publishName);
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="Payload encoding"
|
||||
name="encoding"
|
||||
rules={[{required: true, message: "Please select an encoding!"}]}
|
||||
rules={[{ required: true, message: "Please select an encoding!" }]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value={Encoding.JSON}>JSON</Select.Option>
|
||||
@ -44,19 +39,21 @@ class AzureServiceBusIntegrationForm extends Component<IProps> {
|
||||
label="Azure Service-Bus connection string"
|
||||
name="connectionString"
|
||||
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 />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Azure Service-Bus topic / queue name"
|
||||
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 />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -24,14 +24,16 @@ class CreateAwsSnsIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createAwsSnsIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new AwsSnsIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add AWS SNS integration">
|
||||
<AwsSnsIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -12,12 +12,10 @@ import {
|
||||
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreateAzureServiceBusIntegration extends Component<IProps> {
|
||||
onFinish = (obj: AzureServiceBusIntegration) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -26,14 +24,16 @@ class CreateAzureServiceBusIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createAzureServiceBusIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new AzureServiceBusIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add Azure Service-Bus integration">
|
||||
<AzureServiceBusIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -12,12 +12,10 @@ import {
|
||||
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreateGcpPubSubIntegration extends Component<IProps> {
|
||||
onFinish = (obj: GcpPubSubIntegration) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -26,14 +24,16 @@ class CreateGcpPubSubIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createGcpPubSubIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new GcpPubSubIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add GCP Pub/Sub integration">
|
||||
<GcpPubSubIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
import HttpIntegrationForm from "./HttpIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
@ -25,14 +24,16 @@ class CreateHttpIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createHttpIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new HttpIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add HTTP integration">
|
||||
<HttpIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -12,12 +12,10 @@ import {
|
||||
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreateInfluxDbIntegration extends Component<IProps> {
|
||||
onFinish = (obj: InfluxDbIntegration) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -26,14 +24,16 @@ class CreateInfluxDbIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createInfluxDbIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new InfluxDbIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add InfluxDB integration">
|
||||
<InfluxDbIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -10,16 +10,13 @@ import {
|
||||
CreateLoraCloudIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
|
||||
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreateLoRaCloudIntegration extends Component<IProps> {
|
||||
onFinish = (obj: LoraCloudIntegration) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -28,9 +25,11 @@ class CreateLoRaCloudIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createLoraCloudIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let i = new LoraCloudIntegration();
|
||||
@ -41,8 +40,7 @@ class CreateLoRaCloudIntegration extends Component<IProps> {
|
||||
|
||||
i.setModemGeolocationServices(mgs);
|
||||
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add Semtech LoRa Cloud™ integration">
|
||||
<LoRaCloudIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -12,12 +12,10 @@ import {
|
||||
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreateMyDevicesIntegration extends Component<IProps> {
|
||||
onFinish = (obj: MyDevicesIntegration) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -26,14 +24,16 @@ class CreateMyDevicesIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createMyDevicesIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new MyDevicesIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add myDevices integration">
|
||||
<MyDevicesIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -12,12 +12,10 @@ import {
|
||||
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreatePilotThingsIntegration extends Component<IProps> {
|
||||
onFinish = (obj: PilotThingsIntegration) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -26,14 +24,16 @@ class CreatePilotThingsIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createPilotThingsIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new PilotThingsIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add Pilot Things integration">
|
||||
<PilotThingsIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -12,12 +12,10 @@ import {
|
||||
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreateThingsBoardIntegration extends Component<IProps> {
|
||||
onFinish = (obj: ThingsBoardIntegration) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -26,14 +24,16 @@ class CreateThingsBoardIntegration extends Component<IProps> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.createThingsBoardIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const i = new ThingsBoardIntegration();
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Add ThingsBoard integration">
|
||||
<ThingsBoardIntegrationForm initialValues={i} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -22,7 +22,6 @@ interface IState {
|
||||
integration?: AwsSnsIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditAwsSnsIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -45,16 +44,18 @@ class EditAwsSnsIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateAwsSnsIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update AWS SNS integration">
|
||||
<AwsSnsIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -14,17 +14,14 @@ import {
|
||||
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
integration?: AzureServiceBusIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditAzureServiceBusIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -47,16 +44,18 @@ class EditAzureServiceBusIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateAzureServiceBusIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update Azure Service-Bus integration">
|
||||
<AzureServiceBusIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -14,17 +14,14 @@ import {
|
||||
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
integration?: GcpPubSubIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditGcpPubSubIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -47,19 +44,21 @@ class EditGcpPubSubIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateGcpPubSubIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
<Card title="Update GCP Pub/Sub integration">
|
||||
<GcpPubSubIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
return (
|
||||
<Card title="Update GCP Pub/Sub integration">
|
||||
<GcpPubSubIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
import HttpIntegrationForm from "./HttpIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
@ -45,16 +44,18 @@ class EditHttpIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateHttpIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update HTTP integration">
|
||||
<HttpIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -14,17 +14,14 @@ import {
|
||||
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
integration?: InfluxDbIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditInfluxDbIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -47,17 +44,18 @@ class EditInfluxDbIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateInfluxDbIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update InfluxDB integration">
|
||||
<InfluxDbIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -14,17 +14,14 @@ import {
|
||||
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
integration?: LoraCloudIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditLoRaCloudIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -47,16 +44,18 @@ class EditLoRaCloudIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateLoraCloudIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update Semtech LoRa Cloud™ integration">
|
||||
<LoRaCloudIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
@ -23,7 +22,6 @@ interface IState {
|
||||
integration?: MyDevicesIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditMyDevicesIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -46,16 +44,18 @@ class EditMyDevicesIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateMyDevicesIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update myDevices integration">
|
||||
<MyDevicesIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -14,17 +14,14 @@ import {
|
||||
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
integration?: PilotThingsIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditPilotThingsIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -47,16 +44,18 @@ class EditPilotThingsIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updatePilotThingsIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update Pilot Things integration">
|
||||
<PilotThingsIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
application: Application;
|
||||
}
|
||||
@ -23,7 +22,6 @@ interface IState {
|
||||
integration?: ThingsBoardIntegration;
|
||||
}
|
||||
|
||||
|
||||
class EditThingsBoardIntegration extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -46,16 +44,18 @@ class EditThingsBoardIntegration extends Component<IProps, IState> {
|
||||
req.setIntegration(obj);
|
||||
|
||||
ApplicationStore.updateThingsBoardIntegration(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/integrations`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.integration === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Card title="Update ThingsBoard integration">
|
||||
<ThingsBoardIntegrationForm initialValues={this.state.integration} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
|
@ -1,44 +1,40 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
Application,
|
||||
DeleteGcpPubSubIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Application, DeleteGcpPubSubIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class GcpPubSubCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteGcpPubSubIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deleteGcpPubSubIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/gcp-pub-sub/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/gcp-pub-sub/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/gcp-pub-sub/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/gcp-pub-sub/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +45,10 @@ class GcpPubSubCard extends Component<IProps> {
|
||||
<Card
|
||||
title="GCP Pub/Sub"
|
||||
className="integration-card"
|
||||
cover={<img alt="GCP Pub/Sub" src="/integrations/gcp_pubsub.png" style={{padding: 1}} />}
|
||||
cover={<img alt="GCP Pub/Sub" src="/integrations/gcp_pubsub.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The Google Cloud Pub/Sub integration forwards events to a GCP Pub/Sub topic."
|
||||
/>
|
||||
<Card.Meta description="The Google Cloud Pub/Sub integration forwards events to a GCP Pub/Sub topic." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -2,18 +2,13 @@ import React, { Component } from "react";
|
||||
|
||||
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";
|
||||
|
||||
interface IProps {
|
||||
initialValues: GcpPubSubIntegration;
|
||||
onFinish: (obj: GcpPubSubIntegration) => void;
|
||||
}
|
||||
|
||||
|
||||
class GcpPubSubIntegrationForm extends Component<IProps> {
|
||||
onFinish = (values: GcpPubSubIntegration.AsObject) => {
|
||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
||||
@ -26,15 +21,15 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
|
||||
i.setCredentialsFile(v.credentialsFile);
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="Payload encoding"
|
||||
name="encoding"
|
||||
rules={[{required: true, message: "Please select an encoding!"}]}
|
||||
rules={[{ required: true, message: "Please select an encoding!" }]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value={Encoding.JSON}>JSON</Select.Option>
|
||||
@ -44,14 +39,14 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
|
||||
<Form.Item
|
||||
label="GCP project ID"
|
||||
name="projectId"
|
||||
rules={[{required: true, message: "Please enter a GCP project ID!"}]}
|
||||
rules={[{ required: true, message: "Please enter a GCP project ID!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="GCP Pub/Sub topic name"
|
||||
name="topicName"
|
||||
rules={[{required: true, message: "Please enter a GCP Pub/Sub topic name!"}]}
|
||||
rules={[{ required: true, message: "Please enter a GCP Pub/Sub topic name!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
@ -59,12 +54,14 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
|
||||
label="GCP Service account credentials file"
|
||||
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."
|
||||
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} />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
import moment from "moment";
|
||||
import { Card, Button, Form, Input } from 'antd';
|
||||
import { Card, Button, Form, Input } from "antd";
|
||||
|
||||
import {
|
||||
Application,
|
||||
@ -11,18 +11,15 @@ import {
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
certificate?: GenerateMqttIntegrationClientCertificateResponse;
|
||||
buttonDisabled: boolean;
|
||||
}
|
||||
|
||||
|
||||
class GenerateMqttCertificate extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -40,28 +37,35 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
|
||||
let req = new GenerateMqttIntegrationClientCertificateRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
|
||||
ApplicationStore.generateMqttIntegrationClientCertificate(req, (resp: GenerateMqttIntegrationClientCertificateResponse) => {
|
||||
this.setState({
|
||||
certificate: resp,
|
||||
});
|
||||
});
|
||||
}
|
||||
ApplicationStore.generateMqttIntegrationClientCertificate(
|
||||
req,
|
||||
(resp: GenerateMqttIntegrationClientCertificateResponse) => {
|
||||
this.setState({
|
||||
certificate: resp,
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
renderRequest = () => {
|
||||
return(
|
||||
return (
|
||||
<Card>
|
||||
<p>
|
||||
If required by the network, the MQTT client needs to be configured with a client certificate
|
||||
in order to connect to the MQTT broker to receive device data. The generated certificate is
|
||||
application specific.
|
||||
<strong> Please note the expiration of the certificate and make sure to renew the certificate on time!</strong>
|
||||
If required by the network, the MQTT client needs to be configured with a client certificate in order to
|
||||
connect to the MQTT broker to receive device data. The generated certificate is application specific.
|
||||
<strong>
|
||||
{" "}
|
||||
Please note the expiration of the certificate and make sure to renew the certificate on time!
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<Button onClick={this.requestCertificate} disabled={this.state.buttonDisabled}>Generate certificate</Button>
|
||||
<Button onClick={this.requestCertificate} disabled={this.state.buttonDisabled}>
|
||||
Generate certificate
|
||||
</Button>
|
||||
</p>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderResponse = () => {
|
||||
const certificate = this.state.certificate!;
|
||||
@ -73,39 +77,31 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
|
||||
tlsKey: certificate.getTlsKey(),
|
||||
};
|
||||
|
||||
return(
|
||||
return (
|
||||
<Form layout="vertical" initialValues={initial}>
|
||||
<Form.Item
|
||||
label="Expiration date"
|
||||
name="expiresAt"
|
||||
tooltip="The certificate expires at this date. Make sure to generate and configure a new certificate for your gateway before this expiration date."
|
||||
>
|
||||
<Input disabled style={{cursor: "text", color: "black"}} />
|
||||
<Input disabled style={{ cursor: "text", color: "black" }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="CA certificate"
|
||||
name="caCert"
|
||||
tooltip="The CA certificate is to authenticate the certificate of the server. Store this as a text-file, e.g. named 'ca.crt'."
|
||||
>
|
||||
<Input.TextArea autoSize disabled style={{cursor: "text", fontFamily: "monospace", color: "black"}} />
|
||||
<Input.TextArea autoSize disabled style={{ cursor: "text", fontFamily: "monospace", color: "black" }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="TLS certificate"
|
||||
name="tlsCert"
|
||||
tooltip="Store this as a text-file, e.g. named 'cert.crt'"
|
||||
>
|
||||
<Input.TextArea autoSize disabled style={{cursor: "text", fontFamily: "monospace", color: "black"}} />
|
||||
<Form.Item label="TLS certificate" name="tlsCert" tooltip="Store this as a text-file, e.g. named 'cert.crt'">
|
||||
<Input.TextArea autoSize disabled style={{ cursor: "text", fontFamily: "monospace", color: "black" }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="TLS key"
|
||||
name="tlsKey"
|
||||
tooltip="Store this as a text-file, e.g. named 'cert.key'"
|
||||
>
|
||||
<Input.TextArea autoSize disabled style={{cursor: "text", fontFamily: "monospace", color: "black"}} />
|
||||
<Form.Item label="TLS key" name="tlsKey" tooltip="Store this as a text-file, e.g. named 'cert.key'">
|
||||
<Input.TextArea autoSize disabled style={{ cursor: "text", fontFamily: "monospace", color: "black" }} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let content = this.renderRequest();
|
||||
@ -114,11 +110,7 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
|
||||
content = this.renderResponse();
|
||||
}
|
||||
|
||||
return(
|
||||
<Card title="Generate MQTT certificate">
|
||||
{content}
|
||||
</Card>
|
||||
);
|
||||
return <Card title="Generate MQTT certificate">{content}</Card>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,44 +1,40 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
Application,
|
||||
DeleteHttpIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Application, DeleteHttpIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class HttpCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteHttpIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deleteHttpIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/http/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/http/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/http/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/http/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +45,10 @@ class HttpCard extends Component<IProps> {
|
||||
<Card
|
||||
title="HTTP"
|
||||
className="integration-card"
|
||||
cover={<img alt="HTTP" src="/integrations/http.png" style={{padding: 1}} />}
|
||||
cover={<img alt="HTTP" src="/integrations/http.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The HTTP integration forwards events to a user-configurable endpoint as POST requests."
|
||||
/>
|
||||
<Card.Meta description="The HTTP integration forwards events to a user-configurable endpoint as POST requests." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -1,13 +1,9 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
import { Form, Input, Button, Select, Row, Col, Typography, Space } from "antd";
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
|
||||
import {
|
||||
HttpIntegration,
|
||||
Encoding,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
|
||||
import { HttpIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
interface IProps {
|
||||
initialValues: HttpIntegration;
|
||||
@ -29,15 +25,15 @@ class HttpIntegrationForm extends Component<IProps> {
|
||||
}
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="Payload encoding"
|
||||
name="encoding"
|
||||
rules={[{required: true, message: "Please select an encoding!"}]}
|
||||
rules={[{ required: true, message: "Please select an encoding!" }]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value={Encoding.JSON}>JSON</Select.Option>
|
||||
@ -48,14 +44,14 @@ class HttpIntegrationForm extends Component<IProps> {
|
||||
label="Event endpoint URL(s)"
|
||||
name="eventEndpointUrl"
|
||||
tooltip="ChirpStack will make a POST request to this URL(s) with 'event' as query parameter. Multiple URLs can be defined as a comma separated list. Whitespace will be automatically removed."
|
||||
rules={[{required: true, message: "Please enter an event endpoint URL!"}]}
|
||||
rules={[{ required: true, message: "Please enter an event endpoint URL!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Space direction="vertical" style={{width: "100%"}}>
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
<Typography.Text>Headers</Typography.Text>
|
||||
<Form.List name="headersMap">
|
||||
{(fields, { add, remove }) => (
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(( {key, name, ...restField} ) => (
|
||||
<Row gutter={24}>
|
||||
@ -64,7 +60,7 @@ class HttpIntegrationForm extends Component<IProps> {
|
||||
{...restField}
|
||||
name={[name, 0]}
|
||||
fieldKey={[name, 0]}
|
||||
rules={[{ required: true, message: 'Please enter a key!' }]}
|
||||
rules={[{ required: true, message: "Please enter a key!" }]}
|
||||
>
|
||||
<Input placeholder="Key" />
|
||||
</Form.Item>
|
||||
@ -74,14 +70,14 @@ class HttpIntegrationForm extends Component<IProps> {
|
||||
{...restField}
|
||||
name={[name, 1]}
|
||||
fieldKey={[name, 1]}
|
||||
rules={[{ required: true, message: 'Please enter a value!' }]}
|
||||
rules={[{ required: true, message: "Please enter a value!" }]}
|
||||
>
|
||||
<Input placeholder="Value" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Form.Item>
|
||||
@ -94,7 +90,9 @@ class HttpIntegrationForm extends Component<IProps> {
|
||||
</Form.List>
|
||||
</Space>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -8,18 +8,15 @@ import {
|
||||
InfluxDbVersion,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
|
||||
interface IProps {
|
||||
initialValues: InfluxDbIntegration;
|
||||
onFinish: (obj: InfluxDbIntegration) => void;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
selectedVersion: InfluxDbVersion;
|
||||
}
|
||||
|
||||
|
||||
class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -45,21 +42,21 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
||||
i.setToken(v.token);
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
onVersionChange = (version: InfluxDbVersion) => {
|
||||
this.setState({
|
||||
selectedVersion: version,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="InfluxDB 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.Option value={InfluxDbVersion.INFLUXDB_1}>InfluxDB v1</Select.Option>
|
||||
@ -69,71 +66,69 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
|
||||
<Form.Item
|
||||
label="API endpoint (write)"
|
||||
name="endpoint"
|
||||
rules={[{required: true, message: "Please enter an endpoint!"}]}
|
||||
rules={[{ required: true, message: "Please enter an endpoint!" }]}
|
||||
>
|
||||
<Input
|
||||
placeholder="http://localhost:8086/api/v2/write"
|
||||
/>
|
||||
<Input placeholder="http://localhost:8086/api/v2/write" />
|
||||
</Form.Item>
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item
|
||||
label="Username"
|
||||
name="username"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item
|
||||
label="Password"
|
||||
name="password"
|
||||
>
|
||||
<Input.Password />
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item
|
||||
label="Database name"
|
||||
name="db"
|
||||
rules={[{required: true, message: "Please enter database name!"}]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item
|
||||
label="Retention policy name"
|
||||
name="retentionPolicyName"
|
||||
tooltip="Sets the target retention policy for the write. InfluxDB writes to the DEFAULT retention policy if you do not specify a retention policy."
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item
|
||||
label="Select timestamp precision"
|
||||
name="precision"
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value={InfluxDbPrecision.NS}>Nanosecond</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.U}>Microsecond</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.MS}>Millisecond</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.S}>Second</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.M}>Minute</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.H}>Hour</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && <Form.Item
|
||||
label="Organization"
|
||||
name="organization"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && <Form.Item
|
||||
label="Bucket"
|
||||
name="bucket"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && <Form.Item
|
||||
label="Token"
|
||||
name="token"
|
||||
>
|
||||
<Input.Password />
|
||||
</Form.Item>}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||
<Form.Item label="Username" name="username">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||
<Form.Item label="Password" name="password">
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||
<Form.Item
|
||||
label="Database name"
|
||||
name="db"
|
||||
rules={[{ required: true, message: "Please enter database name!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||
<Form.Item
|
||||
label="Retention policy name"
|
||||
name="retentionPolicyName"
|
||||
tooltip="Sets the target retention policy for the write. InfluxDB writes to the DEFAULT retention policy if you do not specify a retention policy."
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
|
||||
<Form.Item label="Select timestamp precision" name="precision">
|
||||
<Select>
|
||||
<Select.Option value={InfluxDbPrecision.NS}>Nanosecond</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.U}>Microsecond</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.MS}>Millisecond</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.S}>Second</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.M}>Minute</Select.Option>
|
||||
<Select.Option value={InfluxDbPrecision.H}>Hour</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
||||
<Form.Item label="Organization" name="organization">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
||||
<Form.Item label="Bucket" name="bucket">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
|
||||
<Form.Item label="Token" name="token">
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,44 +1,40 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
Application,
|
||||
DeleteInfluxDbIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Application, DeleteInfluxDbIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class InfluxdbCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteInfluxDbIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deleteInfluxDbIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/influxdb/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/influxdb/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/influxdb/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/influxdb/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +45,10 @@ class InfluxdbCard extends Component<IProps> {
|
||||
<Card
|
||||
title="InfluxDB"
|
||||
className="integration-card"
|
||||
cover={<img alt="InfluxDB" src="/integrations/influxdb.png" style={{padding: 1}} />}
|
||||
cover={<img alt="InfluxDB" src="/integrations/influxdb.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The InfluxDB integration writes events into an InfluxDB time-series database."
|
||||
/>
|
||||
<Card.Meta description="The InfluxDB integration writes events into an InfluxDB time-series database." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -1,44 +1,40 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
Application,
|
||||
DeleteLoraCloudIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Application, DeleteLoraCloudIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class LoRaCloudCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteLoraCloudIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deleteLoraCloudIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/loracloud/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/loracloud/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/loracloud/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/loracloud/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +45,10 @@ class LoRaCloudCard extends Component<IProps> {
|
||||
<Card
|
||||
title="Semtech LoRa Cloud™"
|
||||
className="integration-card"
|
||||
cover={<img alt="Semtech LoRa Cloud" src="/integrations/loracloud.png" style={{padding: 1}} />}
|
||||
cover={<img alt="Semtech LoRa Cloud" src="/integrations/loracloud.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The Semtech LoRa Cloud integration provides Modem & Geolocation Services."
|
||||
/>
|
||||
<Card.Meta description="The Semtech LoRa Cloud integration provides Modem & Geolocation Services." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -7,13 +7,11 @@ import {
|
||||
LoraCloudModemGeolocationServices,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
|
||||
interface IProps {
|
||||
initialValues: LoraCloudIntegration;
|
||||
onFinish: (obj: LoraCloudIntegration) => void;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
modemEnabled: boolean;
|
||||
geolocationTdoa: boolean;
|
||||
@ -22,7 +20,6 @@ interface IState {
|
||||
geolocationGnss: boolean;
|
||||
}
|
||||
|
||||
|
||||
class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -56,7 +53,6 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
||||
|
||||
let mgs = new LoraCloudModemGeolocationServices();
|
||||
|
||||
|
||||
if (mgsv !== undefined) {
|
||||
mgs.setToken(mgsv.token);
|
||||
mgs.setModemEnabled(mgsv.modemEnabled);
|
||||
@ -80,48 +76,48 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
||||
i.setModemGeolocationServices(mgs);
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
onModemEnabledChange = (v: boolean) => {
|
||||
this.setState({
|
||||
modemEnabled: v,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onGeolocationTdoaChange = (v: boolean) => {
|
||||
this.setState({
|
||||
geolocationTdoa: v,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onGeolocationRssiChange = (v: boolean) => {
|
||||
this.setState({
|
||||
geolocationRssi: v,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onGeolocationWifiChange = (v: boolean) => {
|
||||
this.setState({
|
||||
geolocationWifi: v,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onGeolocationGnssChange = (v: boolean) => {
|
||||
this.setState({
|
||||
geolocationGnss: v,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Tabs>
|
||||
<Tabs.TabPane tab="Modem & Geolocation Services" key="1">
|
||||
<Form.Item
|
||||
label="Token"
|
||||
name={["modemGeolocationServices", "token"]}
|
||||
tooltip="This token can be obtained from loracloud.com"
|
||||
rules={[{required: true, message: "Please enter a token!"}]}
|
||||
rules={[{ required: true, message: "Please enter a token!" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
@ -132,39 +128,47 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
||||
>
|
||||
<Switch onChange={this.onModemEnabledChange} />
|
||||
</Form.Item>
|
||||
{this.state.modemEnabled && <Form.Item
|
||||
label="GNSS port (FPort)"
|
||||
name={["modemGeolocationServices", "gnssPort"]}
|
||||
tooltip="ChirpStack will only forward the FRMPayload for GNSS geolocation to LoRa Cloud when the uplink matches the configured port."
|
||||
rules={[{required: true, message: "Please enter a port number!"}]}
|
||||
>
|
||||
<InputNumber min={0} max={255} />
|
||||
</Form.Item>}
|
||||
{this.state.modemEnabled && <Form.Item
|
||||
label="Modem port (FPort)"
|
||||
name={["modemGeolocationServices", "modemPort"]}
|
||||
tooltip="ChirpStack will only forward the FRMPayload to LoRa Cloud when the uplink matches the configured port."
|
||||
rules={[{required: true, message: "Please enter a port number!"}]}
|
||||
>
|
||||
<InputNumber min={0} max={255} />
|
||||
</Form.Item>}
|
||||
{this.state.modemEnabled && <Form.Item
|
||||
label="Use receive timestamp for GNSS geolocation"
|
||||
name={["modemGeolocationServices", "gnssUseRxTime"]}
|
||||
tooltip="If enabled, the receive timestamp of the gateway will be used as reference instead of the timestamp included in the GNSS payload."
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>}
|
||||
{this.state.modemEnabled && <Form.Item
|
||||
label="My device adheres to the LoRa Edge™ Tracker Reference Design protocol"
|
||||
name={["modemGeolocationServices", "parseTlv"]}
|
||||
tooltip="If enabled, ChirpStack Application Server will try to resolve the location of the device if a geolocation payload is detected."
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>}
|
||||
<Collapse style={{marginBottom: 24}}>
|
||||
{this.state.modemEnabled && (
|
||||
<Form.Item
|
||||
label="GNSS port (FPort)"
|
||||
name={["modemGeolocationServices", "gnssPort"]}
|
||||
tooltip="ChirpStack will only forward the FRMPayload for GNSS geolocation to LoRa Cloud when the uplink matches the configured port."
|
||||
rules={[{ required: true, message: "Please enter a port number!" }]}
|
||||
>
|
||||
<InputNumber min={0} max={255} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.modemEnabled && (
|
||||
<Form.Item
|
||||
label="Modem port (FPort)"
|
||||
name={["modemGeolocationServices", "modemPort"]}
|
||||
tooltip="ChirpStack will only forward the FRMPayload to LoRa Cloud when the uplink matches the configured port."
|
||||
rules={[{ required: true, message: "Please enter a port number!" }]}
|
||||
>
|
||||
<InputNumber min={0} max={255} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.modemEnabled && (
|
||||
<Form.Item
|
||||
label="Use receive timestamp for GNSS geolocation"
|
||||
name={["modemGeolocationServices", "gnssUseRxTime"]}
|
||||
tooltip="If enabled, the receive timestamp of the gateway will be used as reference instead of the timestamp included in the GNSS payload."
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.modemEnabled && (
|
||||
<Form.Item
|
||||
label="My device adheres to the LoRa Edge™ Tracker Reference Design protocol"
|
||||
name={["modemGeolocationServices", "parseTlv"]}
|
||||
tooltip="If enabled, ChirpStack Application Server will try to resolve the location of the device if a geolocation payload is detected."
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Collapse style={{ marginBottom: 24 }}>
|
||||
<Collapse.Panel header="Advanced geolocation options" key={1}>
|
||||
<Form.Item
|
||||
label="TDOA based geolocation"
|
||||
@ -198,47 +202,59 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
|
||||
>
|
||||
<Switch onChange={this.onGeolocationGnssChange} />
|
||||
</Form.Item>
|
||||
{(this.state.geolocationTdoa || this.state.geolocationRssi) && <Form.Item
|
||||
label="Geolocation buffer (TTL in seconds)"
|
||||
name={["modemGeolocationServices", "geolocationBufferTtl"]}
|
||||
tooltip="The time in seconds that historical uplinks will be stored in the geolocation buffer. Used for TDOA and RSSI geolocation."
|
||||
>
|
||||
<InputNumber min={0} max={86400} />
|
||||
</Form.Item>}
|
||||
{(this.state.geolocationTdoa || this.state.geolocationRssi) && <Form.Item
|
||||
label="Geolocation min buffer size"
|
||||
name={["modemGeolocationServices", "geolocationMinBufferSize"]}
|
||||
tooltip="The minimum buffer size required before using geolocation. Using multiple uplinks for geolocation can increase the accuracy of the geolocation results. Used for TDOA and RSSI geolocation."
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>}
|
||||
{this.state.geolocationWifi && <Form.Item
|
||||
label="Wifi payload field"
|
||||
name={["modemGeolocationServices", "geolocationWifiPayloadField"]}
|
||||
tooltip="This must match the name of the field in the decoded payload which holds array of Wifi access-points. Each element in the array must contain two keys: 1) macAddress: array of 6 bytes, 2) signalStrength: RSSI of the access-point."
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>}
|
||||
{this.state.geolocationGnss && <Form.Item
|
||||
label="GNSS payload field"
|
||||
name={["modemGeolocationServices", "geolocationGnssPayloadField"]}
|
||||
tooltip="This must match the name of the field in the decoded payload which holds the LR1110 GNSS bytes."
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>}
|
||||
{this.state.geolocationGnss && <Form.Item
|
||||
label="Use receive timestamp for GNSS geolocation"
|
||||
name={["modemGeolocationServices", "geolocationGnssUseRxTime"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>}
|
||||
{(this.state.geolocationTdoa || this.state.geolocationRssi) && (
|
||||
<Form.Item
|
||||
label="Geolocation buffer (TTL in seconds)"
|
||||
name={["modemGeolocationServices", "geolocationBufferTtl"]}
|
||||
tooltip="The time in seconds that historical uplinks will be stored in the geolocation buffer. Used for TDOA and RSSI geolocation."
|
||||
>
|
||||
<InputNumber min={0} max={86400} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{(this.state.geolocationTdoa || this.state.geolocationRssi) && (
|
||||
<Form.Item
|
||||
label="Geolocation min buffer size"
|
||||
name={["modemGeolocationServices", "geolocationMinBufferSize"]}
|
||||
tooltip="The minimum buffer size required before using geolocation. Using multiple uplinks for geolocation can increase the accuracy of the geolocation results. Used for TDOA and RSSI geolocation."
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.geolocationWifi && (
|
||||
<Form.Item
|
||||
label="Wifi payload field"
|
||||
name={["modemGeolocationServices", "geolocationWifiPayloadField"]}
|
||||
tooltip="This must match the name of the field in the decoded payload which holds array of Wifi access-points. Each element in the array must contain two keys: 1) macAddress: array of 6 bytes, 2) signalStrength: RSSI of the access-point."
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.geolocationGnss && (
|
||||
<Form.Item
|
||||
label="GNSS payload field"
|
||||
name={["modemGeolocationServices", "geolocationGnssPayloadField"]}
|
||||
tooltip="This must match the name of the field in the decoded payload which holds the LR1110 GNSS bytes."
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{this.state.geolocationGnss && (
|
||||
<Form.Item
|
||||
label="Use receive timestamp for GNSS geolocation"
|
||||
name={["modemGeolocationServices", "geolocationGnssUseRxTime"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,35 +1,27 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card } from 'antd';
|
||||
|
||||
import {
|
||||
Application,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Col, Card } from "antd";
|
||||
|
||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class HttpCard extends Component<IProps> {
|
||||
render() {
|
||||
let actions: any[] = [
|
||||
<Link to="integrations/mqtt/certificate">Get certificate</Link>
|
||||
];
|
||||
let actions: any[] = [<Link to="integrations/mqtt/certificate">Get certificate</Link>];
|
||||
|
||||
return (
|
||||
<Col span={8}>
|
||||
<Card
|
||||
title="MQTT"
|
||||
className="integration-card"
|
||||
cover={<img alt="MQTT" src="/integrations/mqtt.png" style={{padding: 1}} />}
|
||||
cover={<img alt="MQTT" src="/integrations/mqtt.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The MQTT integration forwards events to a MQTT broker."
|
||||
/>
|
||||
<Card.Meta description="The MQTT integration forwards events to a MQTT broker." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -1,44 +1,40 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
Application,
|
||||
DeleteMyDevicesIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { Application, DeleteMyDevicesIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class MyDevicesCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteMyDevicesIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deleteMyDevicesIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/mydevices/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/mydevices/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/mydevices/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/mydevices/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +45,10 @@ class MyDevicesCard extends Component<IProps> {
|
||||
<Card
|
||||
title="myDevices"
|
||||
className="integration-card"
|
||||
cover={<img alt="myDevices" src="/integrations/my_devices.png" style={{padding: 1}} />}
|
||||
cover={<img alt="myDevices" src="/integrations/my_devices.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The myDevices integration forwards events to the myDevices platform."
|
||||
/>
|
||||
<Card.Meta description="The myDevices integration forwards events to the myDevices platform." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -2,23 +2,18 @@ import React, { Component } from "react";
|
||||
|
||||
import { Form, Input, Button, Select } from "antd";
|
||||
|
||||
import {
|
||||
MyDevicesIntegration,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import { MyDevicesIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
interface IProps {
|
||||
initialValues: MyDevicesIntegration;
|
||||
onFinish: (obj: MyDevicesIntegration) => void;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
selectedEndpoint: string;
|
||||
customEndpoint: string;
|
||||
}
|
||||
|
||||
|
||||
class MyDevicesIntegrationForm extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -40,43 +35,49 @@ class MyDevicesIntegrationForm extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
onEndpointChange = (v: string) => {
|
||||
this.setState({
|
||||
selectedEndpoint: v,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onCustomEndpointChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
customEndpoint: e.target.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="Select myDevices 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.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">IoT in a Box</Select.Option>
|
||||
<Select.Option value="https://lora.iotinabox.com/v1/networks/iotinabox.chirpstackio/uplink">
|
||||
IoT in a Box
|
||||
</Select.Option>
|
||||
<Select.Option value="custom">Custom endpoint URL</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
{this.state.selectedEndpoint === "custom" && <Form.Item
|
||||
label="myDevices API endpoint"
|
||||
name="customEndpoint"
|
||||
rules={[{required: true, message: "Please enter an API endpoint!"}]}
|
||||
>
|
||||
<Input onChange={this.onCustomEndpointChange} />
|
||||
</Form.Item>}
|
||||
{this.state.selectedEndpoint === "custom" && (
|
||||
<Form.Item
|
||||
label="myDevices API endpoint"
|
||||
name="customEndpoint"
|
||||
rules={[{ required: true, message: "Please enter an API endpoint!" }]}
|
||||
>
|
||||
<Input onChange={this.onCustomEndpointChange} />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
@ -11,34 +11,33 @@ import {
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class PilotThingsCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeletePilotThingsIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deletePilotThingsIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/pilot-things/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/pilot-things/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/pilot-things/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/pilot-things/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +48,10 @@ class PilotThingsCard extends Component<IProps> {
|
||||
<Card
|
||||
title="Pilot Things"
|
||||
className="integration-card"
|
||||
cover={<img alt="Pilot Things" src="/integrations/pilot_things.png" style={{padding: 1}} />}
|
||||
cover={<img alt="Pilot Things" src="/integrations/pilot_things.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The Pilot Things integration forwards messages to a Pilot Things instance."
|
||||
/>
|
||||
<Card.Meta description="The Pilot Things integration forwards messages to a Pilot Things instance." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -2,18 +2,13 @@ import React, { Component } from "react";
|
||||
|
||||
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";
|
||||
|
||||
interface IProps {
|
||||
initialValues: PilotThingsIntegration;
|
||||
onFinish: (obj: PilotThingsIntegration) => void;
|
||||
}
|
||||
|
||||
|
||||
class PilotThingsIntegrationForm extends Component<IProps> {
|
||||
onFinish = (values: PilotThingsIntegration.AsObject) => {
|
||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
||||
@ -24,27 +19,29 @@ class PilotThingsIntegrationForm extends Component<IProps> {
|
||||
i.setToken(v.token);
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="Pilot Things server"
|
||||
name="server"
|
||||
rules={[{required: true, message: "Please enter a Pilot Things server!"}]}
|
||||
rules={[{ required: true, message: "Please enter a Pilot Things server!" }]}
|
||||
>
|
||||
<Input placeholder="https://host:port" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Authentication token"
|
||||
name="token"
|
||||
rules={[{required: true, message: "Please enter a Pilot Things token!"}]}
|
||||
rules={[{ required: true, message: "Please enter a Pilot Things token!" }]}
|
||||
>
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,44 +1,43 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Col, Card, Popconfirm } from 'antd';
|
||||
import { Col, Card, Popconfirm } from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
|
||||
import {
|
||||
Application,
|
||||
DeleteThingsBoardIntegrationRequest
|
||||
DeleteThingsBoardIntegrationRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
|
||||
import ApplicationStore from "../../../stores/ApplicationStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application,
|
||||
application: Application;
|
||||
add?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class ThingsBoardCard extends Component<IProps> {
|
||||
onDelete = () => {
|
||||
let req = new DeleteThingsBoardIntegrationRequest();
|
||||
req.setApplicationId(this.props.application.getId());
|
||||
ApplicationStore.deleteThingsBoardIntegration(req, () => {});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let actions: any[] = [];
|
||||
|
||||
if (!!this.props.add) {
|
||||
actions = [
|
||||
<Link to="integrations/thingsboard/create"><PlusOutlined /></Link>
|
||||
<Link to="integrations/thingsboard/create">
|
||||
<PlusOutlined />
|
||||
</Link>,
|
||||
];
|
||||
} else {
|
||||
actions = [
|
||||
<Link to="integrations/thingsboard/edit"><EditOutlined /></Link>,
|
||||
<Popconfirm
|
||||
title="Are you sure you want to delete this integration?"
|
||||
onConfirm={this.onDelete}
|
||||
>
|
||||
<Link to="integrations/thingsboard/edit">
|
||||
<EditOutlined />
|
||||
</Link>,
|
||||
<Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
|
||||
<DeleteOutlined />
|
||||
</Popconfirm>,
|
||||
];
|
||||
@ -49,12 +48,10 @@ class ThingsBoardCard extends Component<IProps> {
|
||||
<Card
|
||||
title="ThingsBoard"
|
||||
className="integration-card"
|
||||
cover={<img alt="ThingsBoard" src="/integrations/thingsboard.png" style={{padding: 1}} />}
|
||||
cover={<img alt="ThingsBoard" src="/integrations/thingsboard.png" style={{ padding: 1 }} />}
|
||||
actions={actions}
|
||||
>
|
||||
<Card.Meta
|
||||
description="The ThingsBoard integration forwards events to a ThingsBoard instance."
|
||||
/>
|
||||
<Card.Meta description="The ThingsBoard integration forwards events to a ThingsBoard instance." />
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
|
@ -2,17 +2,13 @@ import React, { Component } from "react";
|
||||
|
||||
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";
|
||||
|
||||
interface IProps {
|
||||
initialValues: ThingsBoardIntegration;
|
||||
onFinish: (obj: ThingsBoardIntegration) => void;
|
||||
}
|
||||
|
||||
|
||||
class ThingsBoardIntegrationForm extends Component<IProps> {
|
||||
onFinish = (values: ThingsBoardIntegration.AsObject) => {
|
||||
const v = Object.assign(this.props.initialValues.toObject(), values);
|
||||
@ -22,25 +18,28 @@ class ThingsBoardIntegrationForm extends Component<IProps> {
|
||||
i.setServer(v.server);
|
||||
|
||||
this.props.onFinish(i);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
return (
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
|
||||
<Form.Item
|
||||
label="ThingsBoard 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" />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Typography.Paragraph>
|
||||
Each device must have a 'ThingsBoardAccessToken' variable assigned. This access-token is generated by ThingsBoard.
|
||||
Each device must have a 'ThingsBoardAccessToken' variable assigned. This access-token is generated by
|
||||
ThingsBoard.
|
||||
</Typography.Paragraph>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
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 moment from "moment";
|
||||
@ -30,7 +30,7 @@ interface GatewaysMapState {
|
||||
items: GatewayListItem[];
|
||||
}
|
||||
|
||||
class GatewaysMap extends Component<{},GatewaysMapState> {
|
||||
class GatewaysMap extends Component<{}, GatewaysMapState> {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
|
||||
@ -51,17 +51,15 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
|
||||
items: resp.getResultList(),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.items.length === 0) {
|
||||
return(
|
||||
<Empty />
|
||||
);
|
||||
return <Empty />;
|
||||
}
|
||||
|
||||
const boundsOptions: {
|
||||
padding: PointTuple,
|
||||
padding: PointTuple;
|
||||
} = {
|
||||
padding: [50, 50],
|
||||
};
|
||||
@ -79,7 +77,7 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
|
||||
if (item.getLastSeenAt() !== undefined) {
|
||||
let ts = moment(item.getLastSeenAt()!.toDate());
|
||||
lastSeen = ts.fromNow();
|
||||
if (ts.isBefore(moment().subtract(5, 'minutes'))) {
|
||||
if (ts.isBefore(moment().subtract(5, "minutes"))) {
|
||||
color = "red";
|
||||
} else {
|
||||
color = "green";
|
||||
@ -89,15 +87,18 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
|
||||
markers.push(
|
||||
<Marker position={pos} faIcon="wifi" color={color}>
|
||||
<Popup>
|
||||
<Link to={`/tenants/${item.getTenantId()}/gateways/${item.getGatewayId()}`}>{item.getName()}</Link><br />
|
||||
{item.getGatewayId()}<br /><br />
|
||||
<Link to={`/tenants/${item.getTenantId()}/gateways/${item.getGatewayId()}`}>{item.getName()}</Link>
|
||||
<br />
|
||||
{item.getGatewayId()}
|
||||
<br />
|
||||
<br />
|
||||
{lastSeen}
|
||||
</Popup>
|
||||
</Marker>
|
||||
</Marker>,
|
||||
);
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Map height={500} bounds={bounds} boundsOptions={boundsOptions}>
|
||||
{markers}
|
||||
</Map>
|
||||
@ -105,18 +106,18 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface GatewayProps {
|
||||
summary?: GetGatewaysSummaryResponse;
|
||||
}
|
||||
|
||||
class GatewaysActiveInactive extends Component<GatewayProps> {
|
||||
render() {
|
||||
if (this.props.summary === undefined || (
|
||||
this.props.summary.getNeverSeenCount() === 0 &&
|
||||
this.props.summary.getInactiveCount() === 0 &&
|
||||
this.props.summary.getActiveCount() === 0
|
||||
)) {
|
||||
if (
|
||||
this.props.summary === undefined ||
|
||||
(this.props.summary.getNeverSeenCount() === 0 &&
|
||||
this.props.summary.getInactiveCount() === 0 &&
|
||||
this.props.summary.getActiveCount() === 0)
|
||||
) {
|
||||
return <Empty />;
|
||||
}
|
||||
|
||||
@ -124,13 +125,13 @@ class GatewaysActiveInactive extends Component<GatewayProps> {
|
||||
labels: ["Never seen", "Inactive", "Active"],
|
||||
datasets: [
|
||||
{
|
||||
data: [this.props.summary.getNeverSeenCount(), this.props.summary.getInactiveCount(), this.props.summary.getActiveCount()],
|
||||
backgroundColor: [
|
||||
presetPalettes.orange.primary,
|
||||
presetPalettes.red.primary,
|
||||
presetPalettes.green.primary,
|
||||
data: [
|
||||
this.props.summary.getNeverSeenCount(),
|
||||
this.props.summary.getInactiveCount(),
|
||||
this.props.summary.getActiveCount(),
|
||||
],
|
||||
}
|
||||
backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -140,24 +141,22 @@ class GatewaysActiveInactive extends Component<GatewayProps> {
|
||||
animation: false,
|
||||
};
|
||||
|
||||
return(
|
||||
<Doughnut data={data} options={options} className="chart-doughtnut" />
|
||||
);
|
||||
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
|
||||
)) {
|
||||
if (
|
||||
this.props.summary === undefined ||
|
||||
(this.props.summary.getNeverSeenCount() === 0 &&
|
||||
this.props.summary.getInactiveCount() === 0 &&
|
||||
this.props.summary.getActiveCount() === 0)
|
||||
) {
|
||||
return <Empty />;
|
||||
}
|
||||
|
||||
@ -165,13 +164,13 @@ class DevicesActiveInactive extends Component<DeviceProps> {
|
||||
labels: ["Never seen", "Inactive", "Active"],
|
||||
datasets: [
|
||||
{
|
||||
data: [this.props.summary.getNeverSeenCount(), this.props.summary.getInactiveCount(), this.props.summary.getActiveCount()],
|
||||
backgroundColor: [
|
||||
presetPalettes.orange.primary,
|
||||
presetPalettes.red.primary,
|
||||
presetPalettes.green.primary,
|
||||
data: [
|
||||
this.props.summary.getNeverSeenCount(),
|
||||
this.props.summary.getInactiveCount(),
|
||||
this.props.summary.getActiveCount(),
|
||||
],
|
||||
}
|
||||
backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -181,18 +180,30 @@ class DevicesActiveInactive extends Component<DeviceProps> {
|
||||
animation: false,
|
||||
};
|
||||
|
||||
|
||||
return(
|
||||
<Doughnut data={data} options={options} className="chart-doughtnut" />
|
||||
);
|
||||
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DevicesDataRates extends Component<DeviceProps> {
|
||||
getColor = (dr: number) => {
|
||||
return ['#ff5722', '#ff9800', '#ffc107', '#ffeb3b', '#cddc39', '#8bc34a', '#4caf50', '#009688', '#00bcd4', '#03a9f4', '#2196f3', '#3f51b5', '#673ab7', '#9c27b0', '#e91e63'][dr];
|
||||
}
|
||||
return [
|
||||
"#ff5722",
|
||||
"#ff9800",
|
||||
"#ffc107",
|
||||
"#ffeb3b",
|
||||
"#cddc39",
|
||||
"#8bc34a",
|
||||
"#4caf50",
|
||||
"#009688",
|
||||
"#00bcd4",
|
||||
"#03a9f4",
|
||||
"#2196f3",
|
||||
"#3f51b5",
|
||||
"#673ab7",
|
||||
"#9c27b0",
|
||||
"#e91e63",
|
||||
][dr];
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.props.summary === undefined || this.props.summary.getDrCountMap().toArray().length === 0) {
|
||||
@ -200,17 +211,19 @@ class DevicesDataRates extends Component<DeviceProps> {
|
||||
}
|
||||
|
||||
let data: {
|
||||
labels: string[],
|
||||
labels: string[];
|
||||
datasets: {
|
||||
data: number[],
|
||||
backgroundColor: string[],
|
||||
}[],
|
||||
data: number[];
|
||||
backgroundColor: string[];
|
||||
}[];
|
||||
} = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
data: [],
|
||||
backgroundColor: [],
|
||||
}],
|
||||
datasets: [
|
||||
{
|
||||
data: [],
|
||||
backgroundColor: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
for (const elm of this.props.summary.getDrCountMap().toArray()) {
|
||||
@ -225,14 +238,11 @@ class DevicesDataRates extends Component<DeviceProps> {
|
||||
animation: false,
|
||||
};
|
||||
|
||||
return(
|
||||
<Doughnut data={data} options={options} className="chart-doughtnut" />
|
||||
);
|
||||
return <Doughnut data={data} options={options} className="chart-doughtnut" />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface IProps{}
|
||||
interface IProps {}
|
||||
|
||||
interface IState {
|
||||
gatewaysSummary?: GetGatewaysSummaryResponse;
|
||||
@ -260,17 +270,19 @@ class Dashboard extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Network Server</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Dashboard</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="Dashboard"
|
||||
/>
|
||||
<Row gutter={24}>
|
||||
@ -290,7 +302,9 @@ class Dashboard extends Component<IProps, IState> {
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Card title="Gateway map"><GatewaysMap /></Card>
|
||||
<Card title="Gateway map">
|
||||
<GatewaysMap />
|
||||
</Card>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -4,18 +4,21 @@ import { Link, RouteComponentProps } from "react-router-dom";
|
||||
import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
||||
|
||||
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
||||
import { DeviceProfile, CreateDeviceProfileRequest, CreateDeviceProfileResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import {
|
||||
DeviceProfile,
|
||||
CreateDeviceProfileRequest,
|
||||
CreateDeviceProfileResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
|
||||
import DeviceProfileForm from "./DeviceProfileForm";
|
||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
tenant: Tenant,
|
||||
tenant: Tenant;
|
||||
}
|
||||
|
||||
|
||||
class CreateDeviceProfile extends Component<IProps> {
|
||||
onFinish = (obj: DeviceProfile) => {
|
||||
obj.setTenantId(this.props.tenant.getId());
|
||||
@ -26,7 +29,7 @@ class CreateDeviceProfile extends Component<IProps> {
|
||||
DeviceProfileStore.create(req, (_resp: CreateDeviceProfileResponse) => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const codecScript = `// Decode uplink function.
|
||||
@ -71,23 +74,29 @@ function encodeDownlink(input) {
|
||||
deviceProfile.setRegParamsRevision(RegParamsRevision.A);
|
||||
deviceProfile.setFlushQueueOnActivate(true);
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/device-profiles`}>Device profiles</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/device-profiles`}>Device profiles</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Add</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="Add device profile"
|
||||
/>
|
||||
<Card>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
import { Form, Input, Select, InputNumber, Switch, Row, Col, Button, Tabs } from "antd";
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
|
||||
import { DeviceProfile, CodecRuntime } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import { Region, MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
||||
@ -9,7 +9,6 @@ import { ListDeviceProfileAdrAlgorithmsResponse } from "@chirpstack/chirpstack-a
|
||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||
import CodeEditor from "../../components/CodeEditor";
|
||||
|
||||
|
||||
interface IProps {
|
||||
initialValues: DeviceProfile;
|
||||
onFinish: (obj: DeviceProfile) => void;
|
||||
@ -24,7 +23,6 @@ interface IState {
|
||||
adrAlgorithms: [string, string][];
|
||||
}
|
||||
|
||||
|
||||
class DeviceProfileForm extends Component<IProps, IState> {
|
||||
formRef = React.createRef<any>();
|
||||
|
||||
@ -101,51 +99,48 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
this.props.onFinish(dp);
|
||||
}
|
||||
};
|
||||
|
||||
onSupportsOtaaChange = (checked: boolean) => {
|
||||
this.setState({
|
||||
supportsOtaa: checked,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSupportsClassBChnage = (checked: boolean) => {
|
||||
this.setState({
|
||||
supportsClassB: checked,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSupportsClassCChange = (checked: boolean) => {
|
||||
this.setState({
|
||||
supportsClassC: checked,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onPayloadCodecRuntimeChange = (value: CodecRuntime) => {
|
||||
this.setState({
|
||||
payloadCodecRuntime: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const adrOptions = this.state.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(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}>
|
||||
return (
|
||||
<Form
|
||||
layout="vertical"
|
||||
initialValues={this.props.initialValues.toObject()}
|
||||
onFinish={this.onFinish}
|
||||
ref={this.formRef}
|
||||
>
|
||||
<Tabs>
|
||||
<Tabs.TabPane tab="General" 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!" }]}>
|
||||
<Input disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Region"
|
||||
name="region"
|
||||
rules={[{required: true, message: "Please select a region!"}]}
|
||||
>
|
||||
<Form.Item label="Region" name="region" rules={[{ required: true, message: "Please select a region!" }]}>
|
||||
<Select disabled={this.props.disabled}>
|
||||
<Select.Option value={Region.AS923}>AS923</Select.Option>
|
||||
<Select.Option value={Region.AS923_2}>AS923-2</Select.Option>
|
||||
@ -168,7 +163,7 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
label="MAC version"
|
||||
tooltip="The LoRaWAN MAC version supported by the device."
|
||||
name="macVersion"
|
||||
rules={[{required: true, message: "Please select a MAC version!"}]}
|
||||
rules={[{ required: true, message: "Please select a MAC version!" }]}
|
||||
>
|
||||
<Select disabled={this.props.disabled}>
|
||||
<Select.Option value={MacVersion.LORAWAN_1_0_0}>LoRaWAN 1.0.0</Select.Option>
|
||||
@ -185,7 +180,7 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
label="Regional parameters revision"
|
||||
tooltip="Revision of the Regional Parameters specification supported by the device."
|
||||
name="regParamsRevision"
|
||||
rules={[{required: true, message: "Please select a regional parameters revision!"}]}
|
||||
rules={[{ required: true, message: "Please select a regional parameters revision!" }]}
|
||||
>
|
||||
<Select disabled={this.props.disabled}>
|
||||
<Select.Option value={RegParamsRevision.A}>A</Select.Option>
|
||||
@ -202,11 +197,9 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
label="ADR algorithm"
|
||||
tooltip="The ADR algorithm that will be used for controlling the device data-rate."
|
||||
name="adrAlgorithmId"
|
||||
rules={[{required: true, message: "Please select an ADR algorithm!"}]}
|
||||
rules={[{ required: true, message: "Please select an ADR algorithm!" }]}
|
||||
>
|
||||
<Select disabled={this.props.disabled}>
|
||||
{adrOptions}
|
||||
</Select>
|
||||
<Select disabled={this.props.disabled}>{adrOptions}</Select>
|
||||
</Form.Item>
|
||||
<Row gutter={24}>
|
||||
<Col span={8}>
|
||||
@ -241,19 +234,16 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
</Row>
|
||||
</Tabs.TabPane>
|
||||
<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} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
{!this.state.supportsOtaa && <Row>
|
||||
{!this.state.supportsOtaa && (
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="RX1 delay"
|
||||
name="abpRx1Delay"
|
||||
rules={[{required: true, message: "Please enter a RX1 delay!"}]}
|
||||
rules={[{ required: true, message: "Please enter a RX1 delay!" }]}
|
||||
>
|
||||
<InputNumber min={0} max={15} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
@ -263,19 +253,21 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
label="RX1 data-rate offset"
|
||||
tooltip="Please refer the LoRaWAN Regional Parameters specification for valid values."
|
||||
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} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>}
|
||||
{!this.state.supportsOtaa && <Row>
|
||||
</Row>
|
||||
)}
|
||||
{!this.state.supportsOtaa && (
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="RX2 data-rate"
|
||||
tooltip="Please refer the LoRaWAN Regional Parameters specification for valid values."
|
||||
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} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
@ -284,46 +276,43 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
<Form.Item
|
||||
label="RX2 frequency (Hz)"
|
||||
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"}} disabled={this.props.disabled} />
|
||||
<InputNumber min={0} style={{ width: "200px" }} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>}
|
||||
</Row>
|
||||
)}
|
||||
</Tabs.TabPane>
|
||||
<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} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
{this.state.supportsClassB && <Form.Item
|
||||
label="Class-B confirmed downlink timeout (seconds)"
|
||||
tooltip="Class-B timeout (in seconds) for confirmed downlink transmissions."
|
||||
name="classBTimeout"
|
||||
rules={[{required: true, message: "Please enter a Class-B confirmed downlink timeout!"}]}
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>}
|
||||
{this.state.supportsClassB && (
|
||||
<Form.Item
|
||||
label="Class-B confirmed downlink timeout (seconds)"
|
||||
tooltip="Class-B timeout (in seconds) for confirmed downlink transmissions."
|
||||
name="classBTimeout"
|
||||
rules={[{ required: true, message: "Please enter a Class-B confirmed downlink timeout!" }]}
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Tabs.TabPane>
|
||||
<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} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
{this.state.supportsClassC && <Form.Item
|
||||
label="Class-C confirmed downlink timeout (seconds)"
|
||||
tooltip="Class-C timeout (in seconds) for confirmed downlink transmissions."
|
||||
name="classCTimeout"
|
||||
rules={[{required: true, message: "Please enter a Class-C confirmed downlink timeout!"}]}
|
||||
>
|
||||
<InputNumber min={0} disabled={this.props.disabled} />
|
||||
</Form.Item>}
|
||||
{this.state.supportsClassC && (
|
||||
<Form.Item
|
||||
label="Class-C confirmed downlink timeout (seconds)"
|
||||
tooltip="Class-C timeout (in seconds) for confirmed downlink transmissions."
|
||||
name="classCTimeout"
|
||||
rules={[{ required: true, message: "Please enter a Class-C confirmed downlink timeout!" }]}
|
||||
>
|
||||
<InputNumber min={0} disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Codec" key="5">
|
||||
<Form.Item
|
||||
@ -346,7 +335,7 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Tags" key="6">
|
||||
<Form.List name="tagsMap">
|
||||
{(fields, { add, remove }) => (
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(( {key, name, ...restField} ) => (
|
||||
<Row gutter={24}>
|
||||
@ -355,7 +344,7 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
{...restField}
|
||||
name={[name, 0]}
|
||||
fieldKey={[name, 0]}
|
||||
rules={[{ required: true, message: 'Please enter a key!' }]}
|
||||
rules={[{ required: true, message: "Please enter a key!" }]}
|
||||
>
|
||||
<Input placeholder="Key" disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
@ -365,18 +354,24 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
{...restField}
|
||||
name={[name, 1]}
|
||||
fieldKey={[name, 1]}
|
||||
rules={[{ required: true, message: 'Please enter a value!' }]}
|
||||
rules={[{ required: true, message: "Please enter a value!" }]}
|
||||
>
|
||||
<Input placeholder="Value" disabled={this.props.disabled} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button disabled={this.props.disabled} type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
|
||||
<Button
|
||||
disabled={this.props.disabled}
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
Add tag
|
||||
</Button>
|
||||
</Form.Item>
|
||||
@ -386,7 +381,9 @@ class DeviceProfileForm extends Component<IProps, IState> {
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" disabled={this.props.disabled}>Submit</Button>
|
||||
<Button type="primary" htmlType="submit" disabled={this.props.disabled}>
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -4,7 +4,13 @@ import { RouteComponentProps, Link } from "react-router-dom";
|
||||
import { Space, Breadcrumb, Card, Button, PageHeader } from "antd";
|
||||
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import { DeviceProfile, GetDeviceProfileRequest, GetDeviceProfileResponse, UpdateDeviceProfileRequest, DeleteDeviceProfileRequest } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import {
|
||||
DeviceProfile,
|
||||
GetDeviceProfileRequest,
|
||||
GetDeviceProfileResponse,
|
||||
UpdateDeviceProfileRequest,
|
||||
DeleteDeviceProfileRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
|
||||
import DeviceProfileForm from "./DeviceProfileForm";
|
||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||
@ -12,7 +18,6 @@ import SessionStore from "../../stores/SessionStore";
|
||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||
import Admin from "../../components/Admin";
|
||||
|
||||
|
||||
interface IState {
|
||||
deviceProfile?: DeviceProfile;
|
||||
}
|
||||
@ -25,7 +30,6 @@ interface IProps extends RouteComponentProps<MatchParams> {
|
||||
tenant: Tenant;
|
||||
}
|
||||
|
||||
|
||||
class EditDeviceProfile extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -46,7 +50,7 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
||||
deviceProfile: resp.getDeviceProfile(),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onFinish = (obj: DeviceProfile) => {
|
||||
let req = new UpdateDeviceProfileRequest();
|
||||
@ -55,7 +59,7 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
||||
DeviceProfileStore.update(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteDeviceProfile = () => {
|
||||
let req = new DeleteDeviceProfileRequest();
|
||||
@ -64,7 +68,7 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
||||
DeviceProfileStore.delete(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const dp = this.state.deviceProfile;
|
||||
@ -73,42 +77,50 @@ class EditDeviceProfile extends Component<IProps, IState> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const disabled = !(SessionStore.isAdmin() || SessionStore.isTenantAdmin(this.props.tenant.getId()) || SessionStore.isTenantDeviceAdmin(this.props.tenant.getId()));
|
||||
const disabled = !(
|
||||
SessionStore.isAdmin() ||
|
||||
SessionStore.isTenantAdmin(this.props.tenant.getId()) ||
|
||||
SessionStore.isTenantDeviceAdmin(this.props.tenant.getId())
|
||||
);
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/device-profiles`}>Device profiles</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/device-profiles`}>Device profiles</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>{dp.getName()}</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title={dp.getName()}
|
||||
subTitle={`device profile id: ${dp.getId()}`}
|
||||
extra={[
|
||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
||||
<DeleteConfirm
|
||||
typ="device profile"
|
||||
confirm={dp.getName()}
|
||||
onConfirm={this.deleteDeviceProfile}
|
||||
>
|
||||
<Button danger type="primary">Delete device profile</Button>
|
||||
<DeleteConfirm typ="device profile" confirm={dp.getName()} onConfirm={this.deleteDeviceProfile}>
|
||||
<Button danger type="primary">
|
||||
Delete device profile
|
||||
</Button>
|
||||
</DeleteConfirm>
|
||||
</Admin>
|
||||
</Admin>,
|
||||
]}
|
||||
/>
|
||||
<Card>
|
||||
<DeviceProfileForm initialValues={dp} disabled={disabled} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
<Card>
|
||||
<DeviceProfileForm initialValues={dp} disabled={disabled} onFinish={this.onFinish} />
|
||||
</Card>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,11 @@ import { Link } from "react-router-dom";
|
||||
import { Space, Breadcrumb, Button, PageHeader } from "antd";
|
||||
import { ColumnsType } from "antd/es/table";
|
||||
|
||||
import { ListDeviceProfilesRequest, ListDeviceProfilesResponse, DeviceProfileListItem } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import {
|
||||
ListDeviceProfilesRequest,
|
||||
ListDeviceProfilesResponse,
|
||||
DeviceProfileListItem,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import { Region } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
||||
|
||||
@ -13,12 +17,10 @@ import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||
import Admin from "../../components/Admin";
|
||||
|
||||
|
||||
interface IProps {
|
||||
tenant: Tenant;
|
||||
}
|
||||
|
||||
|
||||
class ListDeviceProfiles extends Component<IProps> {
|
||||
columns = (): ColumnsType<DeviceProfileListItem.AsObject> => {
|
||||
return [
|
||||
@ -97,7 +99,7 @@ class ListDeviceProfiles extends Component<IProps> {
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||
let req = new ListDeviceProfilesRequest();
|
||||
@ -109,35 +111,37 @@ class ListDeviceProfiles extends Component<IProps> {
|
||||
const obj = resp.toObject();
|
||||
callbackFunc(obj.totalCount, obj.resultList);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Device profiles</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="Device profiles"
|
||||
extra={[
|
||||
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
|
||||
<Button type="primary"><Link to={`/tenants/${this.props.tenant.getId()}/device-profiles/create`}>Add device profile</Link></Button>
|
||||
</Admin>
|
||||
<Button type="primary">
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/device-profiles/create`}>Add device profile</Link>
|
||||
</Button>
|
||||
</Admin>,
|
||||
]}
|
||||
/>
|
||||
<DataTable
|
||||
columns={this.columns()}
|
||||
getPage={this.getPage}
|
||||
rowKey="id"
|
||||
/>
|
||||
<DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" />
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -6,19 +6,20 @@ import { Space, Breadcrumb, Card, PageHeader } from "antd";
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { CreateDeviceRequest, Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
import { GetDeviceProfileRequest, GetDeviceProfileResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import {
|
||||
GetDeviceProfileRequest,
|
||||
GetDeviceProfileResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
|
||||
import DeviceForm from "./DeviceForm";
|
||||
import DeviceStore from "../../stores/DeviceStore";
|
||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
tenant: Tenant;
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
class CreateDevice extends Component<IProps> {
|
||||
onFinish = (obj: Device) => {
|
||||
obj.setApplicationId(this.props.application.getId());
|
||||
@ -33,38 +34,52 @@ class CreateDevice extends Component<IProps> {
|
||||
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
||||
let dp = resp.getDeviceProfile()!;
|
||||
if (dp.getSupportsOtaa()) {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}/keys`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}/keys`,
|
||||
);
|
||||
} else {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let device = new Device();
|
||||
device.setApplicationId(this.props.application.getId());
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>{this.props.application.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>
|
||||
{this.props.application.getName()}
|
||||
</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>Add device</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title="Add device"
|
||||
/>
|
||||
<Card>
|
||||
|
@ -19,14 +19,12 @@ import AesKeyInput from "../../components/AesKeyInput";
|
||||
import DevAddrInput from "../../components/DevAddrInput";
|
||||
import DeviceStore from "../../stores/DeviceStore";
|
||||
|
||||
|
||||
interface FormProps {
|
||||
initialValues: DeviceActivationPb;
|
||||
device: Device;
|
||||
onFinish: (obj: DeviceActivationPb) => void;
|
||||
}
|
||||
|
||||
|
||||
class LW10DeviceActivationForm extends Component<FormProps> {
|
||||
formRef = React.createRef<any>();
|
||||
|
||||
@ -44,11 +42,16 @@ class LW10DeviceActivationForm extends Component<FormProps> {
|
||||
da.setNFCntDown(v.nFCntDown);
|
||||
|
||||
this.props.onFinish(da);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}>
|
||||
return (
|
||||
<Form
|
||||
layout="vertical"
|
||||
initialValues={this.props.initialValues.toObject()}
|
||||
onFinish={this.onFinish}
|
||||
ref={this.formRef}
|
||||
>
|
||||
<DevAddrInput
|
||||
label="Device address"
|
||||
name="devAddr"
|
||||
@ -73,31 +76,26 @@ class LW10DeviceActivationForm extends Component<FormProps> {
|
||||
/>
|
||||
<Row gutter={24}>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
label="Uplink frame-counter"
|
||||
name="fCntUp"
|
||||
>
|
||||
<Form.Item label="Uplink frame-counter" name="fCntUp">
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
label="Downlink frame-counter"
|
||||
name="nFCntDown"
|
||||
>
|
||||
<Form.Item label="Downlink frame-counter" name="nFCntDown">
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">(Re)activate device</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
(Re)activate device
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LW11DeviceActivationForm extends Component<FormProps> {
|
||||
formRef = React.createRef<any>();
|
||||
|
||||
@ -115,11 +113,16 @@ class LW11DeviceActivationForm extends Component<FormProps> {
|
||||
da.setNFCntDown(v.nFCntDown);
|
||||
|
||||
this.props.onFinish(da);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}>
|
||||
return (
|
||||
<Form
|
||||
layout="vertical"
|
||||
initialValues={this.props.initialValues.toObject()}
|
||||
onFinish={this.onFinish}
|
||||
ref={this.formRef}
|
||||
>
|
||||
<DevAddrInput
|
||||
label="Device address"
|
||||
name="devAddr"
|
||||
@ -158,39 +161,31 @@ class LW11DeviceActivationForm extends Component<FormProps> {
|
||||
/>
|
||||
<Row gutter={24}>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
label="Uplink frame-counter"
|
||||
name="fCntUp"
|
||||
>
|
||||
<Form.Item label="Uplink frame-counter" name="fCntUp">
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
label="Downlink frame-counter (network)"
|
||||
name="nFCntDown"
|
||||
>
|
||||
<Form.Item label="Downlink frame-counter (network)" name="nFCntDown">
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
label="Downlink frame-counter (application)"
|
||||
name="aFCntDown"
|
||||
>
|
||||
<Form.Item label="Downlink frame-counter (application)" name="aFCntDown">
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">(Re)activate device</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
(Re)activate device
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface IProps extends RouteComponentProps {
|
||||
tenant: Tenant;
|
||||
application: Application;
|
||||
@ -198,13 +193,11 @@ interface IProps extends RouteComponentProps {
|
||||
deviceProfile: DeviceProfile;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
deviceActivation?: DeviceActivationPb;
|
||||
deviceActivationRequested: boolean;
|
||||
}
|
||||
|
||||
|
||||
class DeviceActivation extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -231,9 +224,11 @@ class DeviceActivation extends Component<IProps, IState> {
|
||||
req.setDeviceActivation(obj);
|
||||
|
||||
DeviceStore.activate(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${this.props.device.getDevEui()}`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${this.props.device.getDevEui()}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.deviceActivationRequested) {
|
||||
@ -248,10 +243,14 @@ class DeviceActivation extends Component<IProps, IState> {
|
||||
initialValues = this.state.deviceActivation;
|
||||
}
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
{!lw11 && <LW10DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} /> }
|
||||
{lw11 && <LW11DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} /> }
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
{!lw11 && (
|
||||
<LW10DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} />
|
||||
)}
|
||||
{lw11 && (
|
||||
<LW11DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} />
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -12,14 +12,11 @@ import {
|
||||
GetDeviceStatsRequest,
|
||||
GetDeviceStatsResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
import {
|
||||
DeviceProfile,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import { DeviceProfile } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
|
||||
import DeviceStore from "../../stores/DeviceStore";
|
||||
import Heatmap from "../../components/Heatmap";
|
||||
|
||||
|
||||
interface IProps {
|
||||
device: Device;
|
||||
deviceProfile: DeviceProfile;
|
||||
@ -32,7 +29,7 @@ interface IState {
|
||||
statsUpFreq: HeatmapStats[];
|
||||
statsUpDr?: any;
|
||||
statsGwRssi?: any;
|
||||
statsGwSnr?: any,
|
||||
statsGwSnr?: any;
|
||||
}
|
||||
|
||||
interface HeatmapStats {
|
||||
@ -78,7 +75,7 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
lineTension: number;
|
||||
pointBackgroundColor: string;
|
||||
data: number[];
|
||||
}[],
|
||||
}[];
|
||||
} = {
|
||||
labels: [],
|
||||
datasets: [
|
||||
@ -129,28 +126,32 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
let statsGwRssiData: (number | null)[] = [];
|
||||
let statsGwRssi = {
|
||||
labels: statsGwRssiLabels,
|
||||
datasets: [{
|
||||
label: "rssi (reported by gateways)",
|
||||
borderColor: "rgba(33, 150, 243, 1)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0)",
|
||||
lineTension: 0,
|
||||
pointBackgroundColor: "rgba(33, 150, 243, 1)",
|
||||
data: statsGwRssiData,
|
||||
}],
|
||||
datasets: [
|
||||
{
|
||||
label: "rssi (reported by gateways)",
|
||||
borderColor: "rgba(33, 150, 243, 1)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0)",
|
||||
lineTension: 0,
|
||||
pointBackgroundColor: "rgba(33, 150, 243, 1)",
|
||||
data: statsGwRssiData,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let statsGwSnrLabels: string[] = [];
|
||||
let statsGwSnrData: (number | null)[] = [];
|
||||
let statsGwSnr = {
|
||||
labels: statsGwSnrLabels,
|
||||
datasets: [{
|
||||
label: "rssi (reported by gateways)",
|
||||
borderColor: "rgba(33, 150, 243, 1)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0)",
|
||||
lineTension: 0,
|
||||
pointBackgroundColor: "rgba(33, 150, 243, 1)",
|
||||
data: statsGwSnrData,
|
||||
}],
|
||||
datasets: [
|
||||
{
|
||||
label: "rssi (reported by gateways)",
|
||||
borderColor: "rgba(33, 150, 243, 1)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0)",
|
||||
lineTension: 0,
|
||||
pointBackgroundColor: "rgba(33, 150, 243, 1)",
|
||||
data: statsGwSnrData,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let statsUpFreq: HeatmapStats[] = [];
|
||||
@ -160,7 +161,10 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
|
||||
statsUpFreq.push({
|
||||
x: moment(row.getTime()!.toDate()).format("YYYY-MM-DD"),
|
||||
y: row.getRxPacketsPerFrequencyMap().toObject().map(v => [v[0].toString(), v[1]]),
|
||||
y: row
|
||||
.getRxPacketsPerFrequencyMap()
|
||||
.toObject()
|
||||
.map(v => [v[0].toString(), v[1]]),
|
||||
});
|
||||
|
||||
statsErrors.labels.push(moment(row.getTime()!.toDate()).format("YYYY-MM-DD"));
|
||||
@ -187,7 +191,6 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
statsErrorsSet[v[0]].push(v[1]);
|
||||
|
||||
}
|
||||
|
||||
for (const v of row.getRxPacketsPerDrMap().toObject()) {
|
||||
@ -204,7 +207,23 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
let backgroundColors = ['#8bc34a', '#ff5722', '#ff9800', '#ffc107', '#ffeb3b', '#cddc39', '#4caf50', '#009688', '#00bcd4', '#03a9f4', '#2196f3', '#3f51b5', '#673ab7', '#9c27b0', '#e91e63'];
|
||||
let backgroundColors = [
|
||||
"#8bc34a",
|
||||
"#ff5722",
|
||||
"#ff9800",
|
||||
"#ffc107",
|
||||
"#ffeb3b",
|
||||
"#cddc39",
|
||||
"#4caf50",
|
||||
"#009688",
|
||||
"#00bcd4",
|
||||
"#03a9f4",
|
||||
"#2196f3",
|
||||
"#3f51b5",
|
||||
"#673ab7",
|
||||
"#9c27b0",
|
||||
"#e91e63",
|
||||
];
|
||||
Object.entries(statsErrorsSet).forEach(([k, v]) => {
|
||||
statsErrors.datasets.push({
|
||||
label: k,
|
||||
@ -213,7 +232,23 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
});
|
||||
});
|
||||
|
||||
backgroundColors = ['#8bc34a', '#ff5722', '#ff9800', '#ffc107', '#ffeb3b', '#cddc39', '#4caf50', '#009688', '#00bcd4', '#03a9f4', '#2196f3', '#3f51b5', '#673ab7', '#9c27b0', '#e91e63'];
|
||||
backgroundColors = [
|
||||
"#8bc34a",
|
||||
"#ff5722",
|
||||
"#ff9800",
|
||||
"#ffc107",
|
||||
"#ffeb3b",
|
||||
"#cddc39",
|
||||
"#4caf50",
|
||||
"#009688",
|
||||
"#00bcd4",
|
||||
"#03a9f4",
|
||||
"#2196f3",
|
||||
"#3f51b5",
|
||||
"#673ab7",
|
||||
"#9c27b0",
|
||||
"#e91e63",
|
||||
];
|
||||
Object.entries(statsUpDrSet).forEach(([k, v]) => {
|
||||
statsUpDr.datasets.push({
|
||||
label: k,
|
||||
@ -231,8 +266,7 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
statsGwSnr: statsGwSnr,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
render() {
|
||||
const animation: false = false;
|
||||
@ -287,15 +321,21 @@ class DeviceDashboard extends Component<IProps, IState> {
|
||||
lastSeenAt = moment(this.props.lastSeenAt).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<Card>
|
||||
<Descriptions>
|
||||
<Descriptions.Item label="Last seen">{lastSeenAt}</Descriptions.Item>
|
||||
<Descriptions.Item label="Device profile"><Link to={`/tenants/${this.props.deviceProfile.getTenantId()}/device-profiles/${this.props.deviceProfile.getId()}/edit`}>{this.props.deviceProfile.getName()}</Link></Descriptions.Item>
|
||||
<Descriptions.Item label="Enabled">{this.props.device.getIsDisabled() ? "no" : "yes"}</Descriptions.Item>
|
||||
<Descriptions.Item label="Description">{this.props.device.getDescription()}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Descriptions>
|
||||
<Descriptions.Item label="Last seen">{lastSeenAt}</Descriptions.Item>
|
||||
<Descriptions.Item label="Device profile">
|
||||
<Link
|
||||
to={`/tenants/${this.props.deviceProfile.getTenantId()}/device-profiles/${this.props.deviceProfile.getId()}/edit`}
|
||||
>
|
||||
{this.props.deviceProfile.getName()}
|
||||
</Link>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Enabled">{this.props.device.getIsDisabled() ? "no" : "yes"}</Descriptions.Item>
|
||||
<Descriptions.Item label="Description">{this.props.device.getDescription()}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
|
@ -6,14 +6,13 @@ import { StreamDeviceEventsRequest, LogItem } from "@chirpstack/chirpstack-api-g
|
||||
import InternalStore from "../../stores/InternalStore";
|
||||
import LogTable from "../../components/LogTable";
|
||||
|
||||
|
||||
interface IProps {
|
||||
device: Device;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
events: LogItem[];
|
||||
cancelFunc?: () => void,
|
||||
cancelFunc?: () => void;
|
||||
}
|
||||
|
||||
class DeviceEvents extends Component<IProps, IState> {
|
||||
@ -44,7 +43,7 @@ class DeviceEvents extends Component<IProps, IState> {
|
||||
this.setState({
|
||||
cancelFunc: cancelFunc,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMessage = (l: LogItem) => {
|
||||
let events = this.state.events;
|
||||
@ -55,10 +54,10 @@ class DeviceEvents extends Component<IProps, IState> {
|
||||
events: events,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(<LogTable logs={this.state.events} />);
|
||||
return <LogTable logs={this.state.events} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,22 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
import { Form, Input, Row, Col, Button, Tabs } from "antd";
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import { Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
import { ListDeviceProfilesRequest, ListDeviceProfilesResponse, GetDeviceProfileRequest, GetDeviceProfileResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import {
|
||||
ListDeviceProfilesRequest,
|
||||
ListDeviceProfilesResponse,
|
||||
GetDeviceProfileRequest,
|
||||
GetDeviceProfileResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
|
||||
import EuiInput from "../../components/EuiInput";
|
||||
import{ OptionsCallbackFunc, OptionCallbackFunc } from "../../components/Autocomplete";
|
||||
import { OptionsCallbackFunc, OptionCallbackFunc } from "../../components/Autocomplete";
|
||||
import AutocompleteInput from "../../components/AutocompleteInput";
|
||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
tenant: Tenant;
|
||||
initialValues: Device;
|
||||
@ -20,7 +24,6 @@ interface IProps {
|
||||
update?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class DeviceForm extends Component<IProps> {
|
||||
formRef = React.createRef<any>();
|
||||
|
||||
@ -45,7 +48,7 @@ class DeviceForm extends Component<IProps> {
|
||||
}
|
||||
|
||||
this.props.onFinish(d);
|
||||
}
|
||||
};
|
||||
|
||||
getDeviceProfileOptions = (search: string, fn: OptionsCallbackFunc) => {
|
||||
let req = new ListDeviceProfilesRequest();
|
||||
@ -54,10 +57,12 @@ class DeviceForm extends Component<IProps> {
|
||||
req.setLimit(10);
|
||||
|
||||
DeviceProfileStore.list(req, (resp: ListDeviceProfilesResponse) => {
|
||||
const options = resp.getResultList().map((o, i) => {return {label: o.getName(), value: o.getId()}});
|
||||
const options = resp.getResultList().map((o, i) => {
|
||||
return { label: o.getName(), value: o.getId() };
|
||||
});
|
||||
fn(options);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getDeviceProfileOption = (id: string, fn: OptionCallbackFunc) => {
|
||||
let req = new GetDeviceProfileRequest();
|
||||
@ -66,27 +71,25 @@ class DeviceForm extends Component<IProps> {
|
||||
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
|
||||
const dp = resp.getDeviceProfile();
|
||||
if (dp) {
|
||||
fn({label: dp.getName(), value: dp.getId()});
|
||||
fn({ label: dp.getName(), value: dp.getId() });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}>
|
||||
return (
|
||||
<Form
|
||||
layout="vertical"
|
||||
initialValues={this.props.initialValues.toObject()}
|
||||
onFinish={this.onFinish}
|
||||
ref={this.formRef}
|
||||
>
|
||||
<Tabs>
|
||||
<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!" }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Description"
|
||||
name="description"
|
||||
>
|
||||
<Form.Item label="Description" name="description">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
<EuiInput
|
||||
@ -108,7 +111,7 @@ class DeviceForm extends Component<IProps> {
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Tags" key="2">
|
||||
<Form.List name="tagsMap">
|
||||
{(fields, { add, remove }) => (
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(( {key, name, ...restField} ) => (
|
||||
<Row gutter={24}>
|
||||
@ -117,7 +120,7 @@ class DeviceForm extends Component<IProps> {
|
||||
{...restField}
|
||||
name={[name, 0]}
|
||||
fieldKey={[name, 0]}
|
||||
rules={[{ required: true, message: 'Please enter a key!' }]}
|
||||
rules={[{ required: true, message: "Please enter a key!" }]}
|
||||
>
|
||||
<Input placeholder="Key" />
|
||||
</Form.Item>
|
||||
@ -127,14 +130,14 @@ class DeviceForm extends Component<IProps> {
|
||||
{...restField}
|
||||
name={[name, 1]}
|
||||
fieldKey={[name, 1]}
|
||||
rules={[{ required: true, message: 'Please enter a value!' }]}
|
||||
rules={[{ required: true, message: "Please enter a value!" }]}
|
||||
>
|
||||
<Input placeholder="Value" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Form.Item>
|
||||
@ -148,7 +151,7 @@ class DeviceForm extends Component<IProps> {
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Variables" key="3">
|
||||
<Form.List name="variablesMap">
|
||||
{(fields, { add, remove }) => (
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(( {key, name, ...restField} ) => (
|
||||
<Row gutter={24}>
|
||||
@ -157,7 +160,7 @@ class DeviceForm extends Component<IProps> {
|
||||
{...restField}
|
||||
name={[name, 0]}
|
||||
fieldKey={[name, 0]}
|
||||
rules={[{ required: true, message: 'Please enter a key!' }]}
|
||||
rules={[{ required: true, message: "Please enter a key!" }]}
|
||||
>
|
||||
<Input placeholder="Key" />
|
||||
</Form.Item>
|
||||
@ -167,14 +170,14 @@ class DeviceForm extends Component<IProps> {
|
||||
{...restField}
|
||||
name={[name, 1]}
|
||||
fieldKey={[name, 1]}
|
||||
rules={[{ required: true, message: 'Please enter a value!' }]}
|
||||
rules={[{ required: true, message: "Please enter a value!" }]}
|
||||
>
|
||||
<Input placeholder="Value" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Form.Item>
|
||||
@ -188,7 +191,9 @@ class DeviceForm extends Component<IProps> {
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -6,14 +6,13 @@ import { StreamDeviceFramesRequest, LogItem } from "@chirpstack/chirpstack-api-g
|
||||
import InternalStore from "../../stores/InternalStore";
|
||||
import LogTable from "../../components/LogTable";
|
||||
|
||||
|
||||
interface IProps {
|
||||
device: Device;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
frames: LogItem[];
|
||||
cancelFunc?: () => void,
|
||||
cancelFunc?: () => void;
|
||||
}
|
||||
|
||||
class DeviceFrames extends Component<IProps, IState> {
|
||||
@ -44,7 +43,7 @@ class DeviceFrames extends Component<IProps, IState> {
|
||||
this.setState({
|
||||
cancelFunc: cancelFunc,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMessage = (l: LogItem) => {
|
||||
let frames = this.state.frames;
|
||||
@ -55,10 +54,10 @@ class DeviceFrames extends Component<IProps, IState> {
|
||||
frames: frames,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(<LogTable logs={this.state.frames} />);
|
||||
return <LogTable logs={this.state.frames} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,17 @@ import { Space, Breadcrumb, Card, Button, PageHeader, Menu } from "antd";
|
||||
|
||||
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { DeviceProfile, GetDeviceProfileRequest, GetDeviceProfileResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import { Device, GetDeviceRequest, GetDeviceResponse, DeleteDeviceRequest } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
import {
|
||||
DeviceProfile,
|
||||
GetDeviceProfileRequest,
|
||||
GetDeviceProfileResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
|
||||
import {
|
||||
Device,
|
||||
GetDeviceRequest,
|
||||
GetDeviceResponse,
|
||||
DeleteDeviceRequest,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
|
||||
import DeviceStore from "../../stores/DeviceStore";
|
||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||
@ -20,12 +29,11 @@ import DeviceEvents from "./DeviceEvents";
|
||||
import DeviceQueue from "./DeviceQueue";
|
||||
import DeviceActivation from "./DeviceActivation";
|
||||
|
||||
|
||||
interface MatchParams {
|
||||
devEui: string;
|
||||
}
|
||||
|
||||
interface IProps extends RouteComponentProps<MatchParams>{
|
||||
interface IProps extends RouteComponentProps<MatchParams> {
|
||||
tenant: Tenant;
|
||||
application: Application;
|
||||
}
|
||||
@ -36,7 +44,6 @@ interface IState {
|
||||
lastSeenAt?: Date;
|
||||
}
|
||||
|
||||
|
||||
class DeviceLayout extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -52,9 +59,12 @@ class DeviceLayout extends Component<IProps, IState> {
|
||||
req.setDevEui(this.props.match.params.devEui);
|
||||
|
||||
DeviceStore.get(req, (resp: GetDeviceResponse) => {
|
||||
this.setState({
|
||||
device: resp.getDevice(),
|
||||
}, cb);
|
||||
this.setState(
|
||||
{
|
||||
device: resp.getDevice(),
|
||||
},
|
||||
cb,
|
||||
);
|
||||
|
||||
if (resp.getLastSeenAt() !== undefined) {
|
||||
this.setState({
|
||||
@ -62,7 +72,7 @@ class DeviceLayout extends Component<IProps, IState> {
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getDeviceProfile = () => {
|
||||
let req = new GetDeviceProfileRequest();
|
||||
@ -73,7 +83,7 @@ class DeviceLayout extends Component<IProps, IState> {
|
||||
deviceProfile: resp.getDeviceProfile(),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deleteDevice = () => {
|
||||
let req = new DeleteDeviceRequest();
|
||||
@ -82,7 +92,7 @@ class DeviceLayout extends Component<IProps, IState> {
|
||||
DeviceStore.delete(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const device = this.state.device;
|
||||
@ -116,61 +126,137 @@ class DeviceLayout extends Component<IProps, IState> {
|
||||
tab = "frames";
|
||||
}
|
||||
|
||||
return(
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<PageHeader
|
||||
breadcrumbRender={() => <Breadcrumb>
|
||||
breadcrumbRender={() => (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<span>Tenants</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}`}>{this.props.tenant.getName()}</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications`}>Applications</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>{this.props.application.getName()}</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>
|
||||
{this.props.application.getName()}
|
||||
</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span><Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>Devices</Link></span>
|
||||
<span>
|
||||
<Link to={`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`}>
|
||||
Devices
|
||||
</Link>
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<span>{device.getName()}</span>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>}
|
||||
</Breadcrumb>
|
||||
)}
|
||||
title={device.getName()}
|
||||
subTitle={`device eui: ${device.getDevEui()}`}
|
||||
extra={[
|
||||
<DeleteConfirm
|
||||
typ="device"
|
||||
confirm={device.getName()}
|
||||
onConfirm={this.deleteDevice}
|
||||
>
|
||||
<Button danger type="primary">Delete device</Button>
|
||||
</DeleteConfirm>
|
||||
<DeleteConfirm typ="device" confirm={device.getName()} onConfirm={this.deleteDevice}>
|
||||
<Button danger type="primary">
|
||||
Delete device
|
||||
</Button>
|
||||
</DeleteConfirm>,
|
||||
]}
|
||||
/>
|
||||
<Card>
|
||||
<Menu mode="horizontal" selectedKeys={[tab]} style={{marginBottom: 24}}>
|
||||
<Menu.Item key="dashboard"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}`}>Dashboard</Link></Menu.Item>
|
||||
<Menu.Item key="edit"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/edit`}>Configuration</Link></Menu.Item>
|
||||
<Menu.Item key="keys" disabled={!dp.getSupportsOtaa()}><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/keys`}>OTAA keys</Link></Menu.Item>
|
||||
<Menu.Item key="activation"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/activation`}>Activation</Link></Menu.Item>
|
||||
<Menu.Item key="queue"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/queue`}>Queue</Link></Menu.Item>
|
||||
<Menu.Item key="events"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/events`}>Events</Link></Menu.Item>
|
||||
<Menu.Item key="frames"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/frames`}>LoRaWAN frames</Link></Menu.Item>
|
||||
</Menu>
|
||||
<Switch>
|
||||
<Route exact path={this.props.match.path} render={props => <DeviceDashboard device={device} lastSeenAt={this.state.lastSeenAt} deviceProfile={dp} {...props} />} />
|
||||
<Route exact path={`${this.props.match.path}/edit`} render={props => <EditDevice device={device} application={app} tenant={tenant} {...props} />} />
|
||||
<Route 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>
|
||||
<Menu mode="horizontal" selectedKeys={[tab]} style={{ marginBottom: 24 }}>
|
||||
<Menu.Item key="dashboard">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}`}>
|
||||
Dashboard
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="edit">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/edit`}>
|
||||
Configuration
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="keys" disabled={!dp.getSupportsOtaa()}>
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/keys`}>
|
||||
OTAA keys
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="activation">
|
||||
<Link
|
||||
to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/activation`}
|
||||
>
|
||||
Activation
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="queue">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/queue`}>
|
||||
Queue
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="events">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/events`}>
|
||||
Events
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="frames">
|
||||
<Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/frames`}>
|
||||
LoRaWAN frames
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path={this.props.match.path}
|
||||
render={props => (
|
||||
<DeviceDashboard device={device} lastSeenAt={this.state.lastSeenAt} deviceProfile={dp} {...props} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${this.props.match.path}/edit`}
|
||||
render={props => <EditDevice device={device} application={app} tenant={tenant} {...props} />}
|
||||
/>
|
||||
<Route
|
||||
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>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -13,13 +13,13 @@ import {
|
||||
GetDeviceQueueItemsRequest,
|
||||
GetDeviceQueueItemsResponse,
|
||||
FlushDeviceQueueRequest,
|
||||
DeviceQueueItem } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
DeviceQueueItem,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
|
||||
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
|
||||
import DeviceStore from "../../stores/DeviceStore";
|
||||
import CodeEditor from "../../components/CodeEditor";
|
||||
|
||||
|
||||
interface IProps {
|
||||
device: Device;
|
||||
}
|
||||
@ -28,7 +28,6 @@ interface IState {
|
||||
refreshCounter: number;
|
||||
}
|
||||
|
||||
|
||||
class DeviceQueue extends Component<IProps, IState> {
|
||||
formRef = React.createRef<any>();
|
||||
|
||||
@ -97,11 +96,11 @@ class DeviceQueue extends Component<IProps, IState> {
|
||||
dataIndex: "data",
|
||||
key: "data",
|
||||
render: (text, record) => {
|
||||
return Buffer.from(record.data as string, 'base64').toString('hex');
|
||||
return Buffer.from(record.data as string, "base64").toString("hex");
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||
let req = new GetDeviceQueueItemsRequest();
|
||||
@ -111,13 +110,13 @@ class DeviceQueue extends Component<IProps, IState> {
|
||||
const obj = resp.toObject();
|
||||
callbackFunc(obj.totalCount, obj.resultList);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
refreshQueue = () => {
|
||||
this.setState({
|
||||
refreshCounter: this.state.refreshCounter + 1,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
flushQueue = () => {
|
||||
let req = new FlushDeviceQueueRequest();
|
||||
@ -125,7 +124,7 @@ class DeviceQueue extends Component<IProps, IState> {
|
||||
DeviceStore.flushQueue(req, () => {
|
||||
this.refreshQueue();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onEnqueue = (values: any) => {
|
||||
let req = new EnqueueDeviceQueueItemRequest();
|
||||
@ -136,11 +135,11 @@ class DeviceQueue extends Component<IProps, IState> {
|
||||
item.setConfirmed(values.confirmed);
|
||||
|
||||
if (values.hex !== undefined) {
|
||||
item.setData(Buffer.from(values.hex, 'hex'));
|
||||
item.setData(Buffer.from(values.hex, "hex"));
|
||||
}
|
||||
|
||||
if (values.base64 !== undefined) {
|
||||
item.setData(Buffer.from(values.base64, 'base64'));
|
||||
item.setData(Buffer.from(values.base64, "base64"));
|
||||
}
|
||||
|
||||
if (values.json !== undefined) {
|
||||
@ -149,7 +148,7 @@ class DeviceQueue extends Component<IProps, IState> {
|
||||
let struct = Struct.fromJavaScript(obj);
|
||||
|
||||
item.setObject(struct);
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
notification.error({
|
||||
message: "Error",
|
||||
@ -162,19 +161,19 @@ class DeviceQueue extends Component<IProps, IState> {
|
||||
|
||||
req.setItem(item);
|
||||
|
||||
DeviceStore.enqueue(req, (_) => {
|
||||
DeviceStore.enqueue(req, _ => {
|
||||
this.formRef.current.resetFields();
|
||||
this.refreshQueue();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" style={{width: "100%"}} size="large">
|
||||
<Space direction="vertical" style={{ width: "100%" }} size="large">
|
||||
<Card title="Enqueue">
|
||||
<Form layout="horizontal" onFinish={this.onEnqueue} ref={this.formRef} initialValues={{fPort: 1}}>
|
||||
<Form layout="horizontal" onFinish={this.onEnqueue} ref={this.formRef} initialValues={{ fPort: 1 }}>
|
||||
<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">
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
@ -195,24 +194,20 @@ class DeviceQueue extends Component<IProps, IState> {
|
||||
</Form.Item>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="JSON" key="3">
|
||||
<CodeEditor
|
||||
name="json"
|
||||
value="{}"
|
||||
formRef={this.formRef}
|
||||
/>
|
||||
<CodeEditor name="json" value="{}" formRef={this.formRef} />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<Button type="primary" htmlType="submit">Enqueue</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Enqueue
|
||||
</Button>
|
||||
</Form>
|
||||
</Card>
|
||||
<Row justify="end">
|
||||
<Space direction="horizontal" size="large">
|
||||
<Button icon={<RedoOutlined />} onClick={this.refreshQueue}>Reload</Button>
|
||||
<Popconfirm
|
||||
title="Are you sure you want to flush the queue?"
|
||||
placement="left"
|
||||
onConfirm={this.flushQueue}
|
||||
>
|
||||
<Button icon={<RedoOutlined />} onClick={this.refreshQueue}>
|
||||
Reload
|
||||
</Button>
|
||||
<Popconfirm title="Are you sure you want to flush the queue?" placement="left" onConfirm={this.flushQueue}>
|
||||
<Button icon={<DeleteOutlined />}>Flush queue</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
|
@ -14,21 +14,20 @@ interface IProps extends RouteComponentProps {
|
||||
device: Device;
|
||||
}
|
||||
|
||||
|
||||
class EditDevice extends Component<IProps> {
|
||||
onFinish = (obj: Device) => {
|
||||
let req = new UpdateDeviceRequest();
|
||||
req.setDevice(obj);
|
||||
|
||||
DeviceStore.update(req, () => {
|
||||
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}`);
|
||||
this.props.history.push(
|
||||
`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}/devices/${obj.getDevEui()}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
<DeviceForm initialValues={this.props.device} onFinish={this.onFinish} tenant={this.props.tenant} update />
|
||||
);
|
||||
return <DeviceForm initialValues={this.props.device} onFinish={this.onFinish} tenant={this.props.tenant} update />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,20 @@ import moment from "moment";
|
||||
import { Space, Button, Dropdown, Menu, Modal, Select } from "antd";
|
||||
import { ColumnsType } from "antd/es/table";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faPlug, faBatteryFull, faBatteryQuarter, faBatteryHalf, faBatteryThreeQuarters } from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faPlug,
|
||||
faBatteryFull,
|
||||
faBatteryQuarter,
|
||||
faBatteryHalf,
|
||||
faBatteryThreeQuarters,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
|
||||
import { ListDevicesRequest, ListDevicesResponse, DeviceListItem } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
import {
|
||||
ListDevicesRequest,
|
||||
ListDevicesResponse,
|
||||
DeviceListItem,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
import {
|
||||
ListMulticastGroupsRequest,
|
||||
ListMulticastGroupsResponse,
|
||||
@ -21,20 +31,17 @@ import DeviceStore from "../../stores/DeviceStore";
|
||||
import MulticastGroupStore from "../../stores/MulticastGroupStore";
|
||||
import Admin from "../../components/Admin";
|
||||
|
||||
|
||||
interface IProps {
|
||||
application: Application;
|
||||
}
|
||||
|
||||
|
||||
interface IState {
|
||||
selectedRowIds: string[];
|
||||
multicastGroups: MulticastGroupListItem[];
|
||||
mgModalVisible: boolean;
|
||||
mgSelected: string,
|
||||
mgSelected: string;
|
||||
}
|
||||
|
||||
|
||||
class ListDevices extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -71,7 +78,7 @@ class ListDevices extends Component<IProps, IState> {
|
||||
ts.setUTCSeconds(record.lastSeenAt.seconds);
|
||||
return moment(ts).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
return "Never";
|
||||
return "Never";
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -80,7 +87,13 @@ class ListDevices extends Component<IProps, IState> {
|
||||
key: "devEui",
|
||||
width: 250,
|
||||
render: (text, record) => (
|
||||
<Link to={`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/devices/${record.devEui}`}>{text}</Link>
|
||||
<Link
|
||||
to={`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/devices/${
|
||||
record.devEui
|
||||
}`}
|
||||
>
|
||||
{text}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -93,7 +106,9 @@ class ListDevices extends Component<IProps, IState> {
|
||||
dataIndex: "deviceProfileName",
|
||||
key: "deviceProfileName",
|
||||
render: (text, record) => (
|
||||
<Link to={`/tenants/${this.props.application.getTenantId()}/device-profiles/${record.deviceProfileId}/edit`}>{text}</Link>
|
||||
<Link to={`/tenants/${this.props.application.getTenantId()}/device-profiles/${record.deviceProfileId}/edit`}>
|
||||
{text}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -119,7 +134,7 @@ class ListDevices extends Component<IProps, IState> {
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
|
||||
let req = new ListDevicesRequest();
|
||||
@ -131,31 +146,31 @@ class ListDevices extends Component<IProps, IState> {
|
||||
const obj = resp.toObject();
|
||||
callbackFunc(obj.totalCount, obj.resultList);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onRowsSelectChange = (ids: string[]) => {
|
||||
this.setState({
|
||||
selectedRowIds: ids,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
showMgModal = () => {
|
||||
this.setState({
|
||||
mgModalVisible: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
hideMgModal = () => {
|
||||
this.setState({
|
||||
mgModalVisible: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMgSelected = (value: string) => {
|
||||
this.setState({
|
||||
mgSelected: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleMgModalOk = () => {
|
||||
for (let devEui of this.state.selectedRowIds) {
|
||||
@ -169,7 +184,7 @@ class ListDevices extends Component<IProps, IState> {
|
||||
this.setState({
|
||||
mgModalVisible: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const menu = (
|
||||
@ -178,21 +193,42 @@ class ListDevices extends Component<IProps, IState> {
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const mgOptions = this.state.multicastGroups.map((mg, i) => <Select.Option value={mg.getId()}>{mg.getName()}</Select.Option>);
|
||||
const mgOptions = this.state.multicastGroups.map((mg, i) => (
|
||||
<Select.Option value={mg.getId()}>{mg.getName()}</Select.Option>
|
||||
));
|
||||
|
||||
return(
|
||||
<Space direction="vertical" size="large" style={{width: "100%"}}>
|
||||
<Modal title="Add selected devices to multicast-group" visible={this.state.mgModalVisible} onOk={this.handleMgModalOk} onCancel={this.hideMgModal} okButtonProps={{disabled: this.state.mgSelected === ""}}>
|
||||
<Space direction="vertical" size="large" style={{width: "100%"}}>
|
||||
<Select style={{width: "100%"}} onChange={this.onMgSelected} placeholder="Select Multicast-group">
|
||||
return (
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<Modal
|
||||
title="Add selected devices to multicast-group"
|
||||
visible={this.state.mgModalVisible}
|
||||
onOk={this.handleMgModalOk}
|
||||
onCancel={this.hideMgModal}
|
||||
okButtonProps={{ disabled: this.state.mgSelected === "" }}
|
||||
>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<Select style={{ width: "100%" }} onChange={this.onMgSelected} placeholder="Select Multicast-group">
|
||||
{mgOptions}
|
||||
</Select>
|
||||
</Space>
|
||||
</Modal>
|
||||
<Admin tenantId={this.props.application.getTenantId()} isDeviceAdmin>
|
||||
<Space direction="horizontal" style={{float: "right"}}>
|
||||
<Button type="primary"><Link to={`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/devices/create`}>Add device</Link></Button>
|
||||
<Dropdown placement="bottomRight" overlay={menu} trigger={['click']} disabled={this.state.selectedRowIds.length === 0}><Button>Selected devices</Button></Dropdown>
|
||||
<Space direction="horizontal" style={{ float: "right" }}>
|
||||
<Button type="primary">
|
||||
<Link
|
||||
to={`/tenants/${this.props.application.getTenantId()}/applications/${this.props.application.getId()}/devices/create`}
|
||||
>
|
||||
Add device
|
||||
</Link>
|
||||
</Button>
|
||||
<Dropdown
|
||||
placement="bottomRight"
|
||||
overlay={menu}
|
||||
trigger={["click"]}
|
||||
disabled={this.state.selectedRowIds.length === 0}
|
||||
>
|
||||
<Button>Selected devices</Button>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
</Admin>
|
||||
<DataTable
|
||||
|
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