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:
SAGAR PATEL 2022-05-02 15:28:26 +05:30 committed by GitHub
parent d8377cd26a
commit 2ea86b2ca2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
137 changed files with 3139 additions and 2515 deletions

2
ui/.prettierignore Normal file
View File

@ -0,0 +1,2 @@
build
node_modules

13
ui/.prettierrc.json Normal file
View 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
}

View File

@ -44,7 +44,14 @@
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject",
"lint": "prettier --check .",
"format": "prettier --write . && git add -A ."
},
"husky": {
"hooks": {
"pre-commit": "yarn format"
}
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
@ -60,5 +67,9 @@
"not dead", "not dead",
"not op_mini all" "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"
}
} }

View File

@ -1,7 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Router, Route, Switch } from "react-router-dom"; 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"; import { User } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
@ -35,15 +35,12 @@ import SessionStore from "./stores/SessionStore";
import history from "./history"; import history from "./history";
interface IProps {}
interface IProps {
}
interface IState { interface IState {
user?: User; user?: User;
} }
class App extends Component<IProps, IState> { class App extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -72,7 +69,8 @@ class App extends Component<IProps, IState> {
<Switch> <Switch>
<Route exact path="/" component={TenantRedirect} /> <Route exact path="/" component={TenantRedirect} />
<Route exact path="/login" component={Login} /> <Route exact path="/login" component={Login} />
{this.state.user && <Route> {this.state.user && (
<Route>
<Layout.Header className="layout-header"> <Layout.Header className="layout-header">
<Header user={this.state.user} /> <Header user={this.state.user} />
</Layout.Header> </Layout.Header>
@ -80,7 +78,7 @@ class App extends Component<IProps, IState> {
<Layout.Sider width="300" theme="light" className="layout-menu"> <Layout.Sider width="300" theme="light" className="layout-menu">
<Menu /> <Menu />
</Layout.Sider> </Layout.Sider>
<Layout.Content className="layout-content" style={{ padding: '24px 24px 24px' }}> <Layout.Content className="layout-content" style={{ padding: "24px 24px 24px" }}>
<Switch> <Switch>
<Route exact path="/dashboard" component={Dashboard} /> <Route exact path="/dashboard" component={Dashboard} />
@ -98,7 +96,8 @@ class App extends Component<IProps, IState> {
</Switch> </Switch>
</Layout.Content> </Layout.Content>
</Layout> </Layout>
</Route>} </Route>
)}
</Switch> </Switch>
</Router> </Router>
</Layout> </Layout>

View File

@ -2,7 +2,6 @@ import { Component } from "react";
import SessionStore from "../stores/SessionStore"; import SessionStore from "../stores/SessionStore";
interface IProps { interface IProps {
tenantId?: string; tenantId?: string;
isDeviceAdmin?: boolean; isDeviceAdmin?: boolean;
@ -68,14 +67,14 @@ class Admin extends Component<IProps, IState> {
}); });
} }
} }
} };
render() { render() {
if (this.state.admin) { if (this.state.admin) {
return(this.props.children); return this.props.children;
} }
return(null); return null;
} }
} }

View File

@ -4,9 +4,9 @@ import { Input, Select, Button, Space, Form } from "antd";
import { ReloadOutlined } from "@ant-design/icons"; import { ReloadOutlined } from "@ant-design/icons";
interface IProps { interface IProps {
formRef: React.RefObject<any>, formRef: React.RefObject<any>;
label: string, label: string;
name: string, name: string;
required?: boolean; required?: boolean;
value?: string; value?: string;
disabled?: boolean; disabled?: boolean;
@ -18,7 +18,6 @@ interface IState {
value: string; value: string;
} }
class AesKeyInput extends Component<IProps, IState> { class AesKeyInput extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -39,7 +38,7 @@ class AesKeyInput extends Component<IProps, IState> {
this.props.formRef.current.setFieldsValue({ this.props.formRef.current.setFieldsValue({
[this.props.name]: value, [this.props.name]: value,
}); });
} };
componentDidMount() { componentDidMount() {
if (this.props.value) { if (this.props.value) {
@ -62,10 +61,13 @@ class AesKeyInput extends Component<IProps, IState> {
} }
} }
this.setState({ this.setState(
{
value: value, value: value,
}, this.updateField); },
} this.updateField,
);
};
onByteOrderSelect = (v: string) => { onByteOrderSelect = (v: string) => {
if (v === this.state.byteOrder) { if (v === this.state.byteOrder) {
@ -79,21 +81,27 @@ class AesKeyInput extends Component<IProps, IState> {
const current = this.state.value; const current = this.state.value;
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || []; const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
this.setState({ this.setState(
{
value: bytes.reverse().join(""), value: bytes.reverse().join(""),
}, this.updateField); },
} this.updateField,
);
};
generateRandom = () => { generateRandom = () => {
let cryptoObj = window.crypto || window.Crypto; let cryptoObj = window.crypto || window.Crypto;
let b = new Uint8Array(16); let b = new Uint8Array(16);
cryptoObj.getRandomValues(b); cryptoObj.getRandomValues(b);
let key = Buffer.from(b).toString('hex'); let key = Buffer.from(b).toString("hex");
this.setState({ this.setState(
{
value: key, value: key,
}, this.updateField); },
} this.updateField,
);
};
render() { render() {
const addon = ( const addon = (
@ -102,23 +110,34 @@ class AesKeyInput extends Component<IProps, IState> {
<Select.Option value="msb">MSB</Select.Option> <Select.Option value="msb">MSB</Select.Option>
<Select.Option value="lsb">LSB</Select.Option> <Select.Option value="lsb">LSB</Select.Option>
</Select> </Select>
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button> <Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
<ReloadOutlined />
</Button>
</Space> </Space>
); );
return ( return (
<Form.Item <Form.Item
rules={[{ rules={[
{
required: this.props.required, required: this.props.required,
message: `Please enter a valid ${this.props.label}`, message: `Please enter a valid ${this.props.label}`,
pattern: new RegExp(/[A-Fa-f0-9]{32}/g), pattern: new RegExp(/[A-Fa-f0-9]{32}/g),
}]} },
]}
label={this.props.label} label={this.props.label}
name={this.props.name} name={this.props.name}
tooltip={this.props.tooltip} tooltip={this.props.tooltip}
> >
<Input hidden /> <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> </Form.Item>
); );
} }

View File

@ -2,24 +2,23 @@ import React, { Component } from "react";
import { Select } from "antd"; import { Select } from "antd";
export type OptionsCallbackFunc = (o: {label: string, value: string}[]) => void; export type OptionsCallbackFunc = (o: { label: string; value: string }[]) => void;
export type OptionCallbackFunc = (o: {label: string, value: string}) => void; export type OptionCallbackFunc = (o: { label: string; value: string }) => void;
interface IProps { interface IProps {
placeholder: string; placeholder: string;
className: string; className: string;
value?: string, value?: string;
getOption: (s: string, fn: OptionCallbackFunc) => void, getOption: (s: string, fn: OptionCallbackFunc) => void;
getOptions: (s: string, fn: OptionsCallbackFunc) => void, getOptions: (s: string, fn: OptionsCallbackFunc) => void;
onSelect?: (s: string) => void, onSelect?: (s: string) => void;
} }
interface IState { interface IState {
option?: {label: string, value: string}; option?: { label: string; value: string };
options: {label: string, value: string}[]; options: { label: string; value: string }[];
} }
class Autocomplete extends Component<IProps, IState> { class Autocomplete extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -31,7 +30,7 @@ class Autocomplete extends Component<IProps, IState> {
componentDidMount() { componentDidMount() {
if (this.props.value && this.props.value !== "") { 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({ this.setState({
options: [o], options: [o],
}); });
@ -45,7 +44,7 @@ class Autocomplete extends Component<IProps, IState> {
} }
if (this.props.value && this.props.value !== "") { 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({ this.setState({
options: [o], options: [o],
}); });
@ -54,7 +53,7 @@ class Autocomplete extends Component<IProps, IState> {
} }
onFocus = () => { onFocus = () => {
this.props.getOptions("", (options) => { this.props.getOptions("", options => {
if (this.state.option !== undefined) { if (this.state.option !== undefined) {
const selected = this.state.option.value; const selected = this.state.option.value;
@ -67,15 +66,15 @@ class Autocomplete extends Component<IProps, IState> {
options: options, options: options,
}); });
}); });
} };
onSearch = (value: string) => { onSearch = (value: string) => {
this.props.getOptions(value, (options) => { this.props.getOptions(value, options => {
this.setState({ this.setState({
options: options, options: options,
}); });
}); });
} };
onSelect = (value: string, option: any) => { onSelect = (value: string, option: any) => {
this.setState({ this.setState({
@ -85,7 +84,7 @@ class Autocomplete extends Component<IProps, IState> {
if (this.props.onSelect !== undefined) { if (this.props.onSelect !== undefined) {
this.props.onSelect(value); this.props.onSelect(value);
} }
} };
render() { render() {
const { getOption, getOptions, ...otherProps } = this.props; const { getOption, getOptions, ...otherProps } = this.props;

View File

@ -4,26 +4,26 @@ import { Form } from "antd";
import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "./Autocomplete"; import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "./Autocomplete";
interface IProps { interface IProps {
formRef: React.RefObject<any>, formRef: React.RefObject<any>;
label: string, label: string;
name: string, name: string;
required?: boolean; required?: boolean;
value?: string; value?: string;
getOption: (s: string, fn: OptionCallbackFunc) => void, getOption: (s: string, fn: OptionCallbackFunc) => void;
getOptions: (s: string, fn: OptionsCallbackFunc) => void, getOptions: (s: string, fn: OptionsCallbackFunc) => void;
} }
class AutocompleteInput extends Component<IProps> { class AutocompleteInput extends Component<IProps> {
render() { render() {
return ( return (
<Form.Item <Form.Item
rules={[{ rules={[
{
required: this.props.required, required: this.props.required,
message: `Please select a ${this.props.label}`, message: `Please select a ${this.props.label}`,
}]} },
]}
label={this.props.label} label={this.props.label}
name={this.props.name} name={this.props.name}
> >

View File

@ -5,11 +5,10 @@ import { Form } from "antd";
import "codemirror/mode/javascript/javascript"; import "codemirror/mode/javascript/javascript";
interface IProps { interface IProps {
formRef: React.RefObject<any>, formRef: React.RefObject<any>;
label?: string, label?: string;
name: string, name: string;
required?: boolean; required?: boolean;
value?: string; value?: string;
disabled?: boolean; disabled?: boolean;
@ -20,7 +19,6 @@ interface IState {
value: string; value: string;
} }
class CodeEditor extends Component<IProps, IState> { class CodeEditor extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -43,13 +41,16 @@ class CodeEditor extends Component<IProps, IState> {
this.props.formRef.current.setFieldsValue({ this.props.formRef.current.setFieldsValue({
[this.props.name]: value, [this.props.name]: value,
}); });
} };
handleChange = (editor: any, data: any, newCode: string) => { handleChange = (editor: any, data: any, newCode: string) => {
this.setState({ this.setState(
{
value: newCode, value: newCode,
}, this.updateField); },
} this.updateField,
);
};
render() { render() {
const codeMirrorOptions = { const codeMirrorOptions = {
@ -60,21 +61,13 @@ class CodeEditor extends Component<IProps, IState> {
}; };
return ( return (
<Form.Item <Form.Item label={this.props.label} name={this.props.name} tooltip={this.props.tooltip}>
label={this.props.label}
name={this.props.name}
tooltip={this.props.tooltip}
>
<div style={{ border: "1px solid #cccccc" }}> <div style={{ border: "1px solid #cccccc" }}>
<CodeMirror <CodeMirror value={this.state.value} options={codeMirrorOptions} onBeforeChange={this.handleChange} />
value={this.state.value}
options={codeMirrorOptions}
onBeforeChange={this.handleChange}
/>
</div> </div>
</Form.Item> </Form.Item>
); );
} }
} }
export default CodeEditor export default CodeEditor;

View File

@ -5,10 +5,8 @@ import { ColumnsType } from "antd/es/table";
import SessionStore from "../stores/SessionStore"; import SessionStore from "../stores/SessionStore";
export type GetPageCallbackFunc = (totalCount: number, rows: object[]) => void; export type GetPageCallbackFunc = (totalCount: number, rows: object[]) => void;
interface IProps { interface IProps {
columns: ColumnsType<any>; columns: ColumnsType<any>;
getPage: (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => void; getPage: (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => void;
@ -23,10 +21,9 @@ interface IState {
pageSize: number; pageSize: number;
currentPage: number; currentPage: number;
rows: object[]; rows: object[];
loading: boolean, loading: boolean;
} }
class DataTable extends Component<IProps, IState> { class DataTable extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -53,9 +50,11 @@ class DataTable extends Component<IProps, IState> {
} }
onChangePage = (page: number, pageSize?: number | void) => { onChangePage = (page: number, pageSize?: number | void) => {
this.setState({ this.setState(
{
loading: true, loading: true,
}, () => { },
() => {
let pz = pageSize; let pz = pageSize;
if (!pz) { if (!pz) {
pz = this.state.pageSize; pz = this.state.pageSize;
@ -70,20 +69,21 @@ class DataTable extends Component<IProps, IState> {
loading: false, loading: false,
}); });
}); });
}); },
} );
};
onShowSizeChange = (page: number, pageSize: number) => { onShowSizeChange = (page: number, pageSize: number) => {
this.onChangePage(page, pageSize); this.onChangePage(page, pageSize);
SessionStore.setRowsPerPage(pageSize); SessionStore.setRowsPerPage(pageSize);
} };
onRowsSelectChange = (ids: React.Key[]) => { onRowsSelectChange = (ids: React.Key[]) => {
const idss = ids as string[]; const idss = ids as string[];
if (this.props.onRowsSelectChange) { if (this.props.onRowsSelectChange) {
this.props.onRowsSelectChange(idss); this.props.onRowsSelectChange(idss);
} }
} };
render() { render() {
const { getPage, refreshKey, ...otherProps } = this.props; const { getPage, refreshKey, ...otherProps } = this.props;
@ -113,7 +113,6 @@ class DataTable extends Component<IProps, IState> {
}; };
} }
return ( return (
<Table <Table
loading={loadingProps} loading={loadingProps}

View File

@ -1,7 +1,6 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Popover, Button, Typography, Space, Input } from 'antd'; import { Popover, Button, Typography, Space, Input } from "antd";
interface IProps { interface IProps {
typ: string; typ: string;
@ -9,17 +8,15 @@ interface IProps {
onConfirm: () => void; onConfirm: () => void;
} }
interface ConfirmState { interface ConfirmState {
confirm: string; confirm: string;
} }
class DeleteConfirmContent extends Component<IProps, ConfirmState> { class DeleteConfirmContent extends Component<IProps, ConfirmState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
this.state = { this.state = {
confirm: '', confirm: "",
}; };
} }
@ -27,20 +24,27 @@ class DeleteConfirmContent extends Component<IProps, ConfirmState> {
this.setState({ this.setState({
confirm: e.target.value, confirm: e.target.value,
}); });
} };
render() { render() {
return ( return (
<Space direction="vertical"> <Space direction="vertical">
<Typography.Text>Enter '{this.props.confirm}' to confirm you want to delete this {this.props.typ}:</Typography.Text> <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} /> <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> <Button
onClick={this.props.onConfirm}
disabled={this.state.confirm !== this.props.confirm}
style={{ float: "right" }}
>
Delete
</Button>
</Space> </Space>
); );
} }
} }
class DeleteConfirm extends Component<IProps> { class DeleteConfirm extends Component<IProps> {
render() { render() {
return ( return (

View File

@ -3,21 +3,15 @@ import React, { Component } from "react";
import { Input, Select, Button, Space, Form } from "antd"; import { Input, Select, Button, Space, Form } from "antd";
import { ReloadOutlined } from "@ant-design/icons"; 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"; import DeviceStore from "../stores/DeviceStore";
interface IProps { interface IProps {
formRef: React.RefObject<any>, formRef: React.RefObject<any>;
label: string, label: string;
name: string, name: string;
devEui: string, devEui: string;
required?: boolean; required?: boolean;
value?: string; value?: string;
disabled?: boolean; disabled?: boolean;
@ -28,7 +22,6 @@ interface IState {
value: string; value: string;
} }
class DevAddrInput extends Component<IProps, IState> { class DevAddrInput extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -49,7 +42,7 @@ class DevAddrInput extends Component<IProps, IState> {
this.props.formRef.current.setFieldsValue({ this.props.formRef.current.setFieldsValue({
[this.props.name]: value, [this.props.name]: value,
}); });
} };
componentDidMount() { componentDidMount() {
if (this.props.value) { if (this.props.value) {
@ -72,10 +65,13 @@ class DevAddrInput extends Component<IProps, IState> {
} }
} }
this.setState({ this.setState(
{
value: value, value: value,
}, this.updateField); },
} this.updateField,
);
};
onByteOrderSelect = (v: string) => { onByteOrderSelect = (v: string) => {
if (v === this.state.byteOrder) { if (v === this.state.byteOrder) {
@ -89,21 +85,27 @@ class DevAddrInput extends Component<IProps, IState> {
const current = this.state.value; const current = this.state.value;
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || []; const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
this.setState({ this.setState(
{
value: bytes.reverse().join(""), value: bytes.reverse().join(""),
}, this.updateField); },
} this.updateField,
);
};
generateRandom = () => { generateRandom = () => {
let req = new GetRandomDevAddrRequest(); let req = new GetRandomDevAddrRequest();
req.setDevEui(this.props.devEui); req.setDevEui(this.props.devEui);
DeviceStore.getRandomDevAddr(req, (resp: GetRandomDevAddrResponse) => { DeviceStore.getRandomDevAddr(req, (resp: GetRandomDevAddrResponse) => {
this.setState({ this.setState(
{
value: resp.getDevAddr(), value: resp.getDevAddr(),
}, this.updateField); },
this.updateField,
);
}); });
} };
render() { render() {
const addon = ( const addon = (
@ -112,22 +114,33 @@ class DevAddrInput extends Component<IProps, IState> {
<Select.Option value="msb">MSB</Select.Option> <Select.Option value="msb">MSB</Select.Option>
<Select.Option value="lsb">LSB</Select.Option> <Select.Option value="lsb">LSB</Select.Option>
</Select> </Select>
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button> <Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
<ReloadOutlined />
</Button>
</Space> </Space>
); );
return ( return (
<Form.Item <Form.Item
rules={[{ rules={[
{
required: this.props.required, required: this.props.required,
message: `Please enter a valid ${this.props.label}`, message: `Please enter a valid ${this.props.label}`,
pattern: new RegExp(/[A-Fa-f0-9]{8}/g), pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
}]} },
]}
label={this.props.label} label={this.props.label}
name={this.props.name} name={this.props.name}
> >
<Input hidden /> <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> </Form.Item>
); );
} }

View File

@ -4,9 +4,9 @@ import { Input, Select, Button, Space, Form } from "antd";
import { ReloadOutlined } from "@ant-design/icons"; import { ReloadOutlined } from "@ant-design/icons";
interface IProps { interface IProps {
formRef: React.RefObject<any>, formRef: React.RefObject<any>;
label: string, label: string;
name: string, name: string;
required?: boolean; required?: boolean;
value?: string; value?: string;
disabled?: boolean; disabled?: boolean;
@ -17,7 +17,6 @@ interface IState {
value: string; value: string;
} }
class EuiInput extends Component<IProps, IState> { class EuiInput extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -38,7 +37,7 @@ class EuiInput extends Component<IProps, IState> {
this.props.formRef.current.setFieldsValue({ this.props.formRef.current.setFieldsValue({
[this.props.name]: value, [this.props.name]: value,
}); });
} };
componentDidMount() { componentDidMount() {
if (this.props.value) { if (this.props.value) {
@ -61,10 +60,13 @@ class EuiInput extends Component<IProps, IState> {
} }
} }
this.setState({ this.setState(
{
value: value, value: value,
}, this.updateField); },
} this.updateField,
);
};
onByteOrderSelect = (v: string) => { onByteOrderSelect = (v: string) => {
if (v === this.state.byteOrder) { if (v === this.state.byteOrder) {
@ -78,21 +80,27 @@ class EuiInput extends Component<IProps, IState> {
const current = this.state.value; const current = this.state.value;
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || []; const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
this.setState({ this.setState(
{
value: bytes.reverse().join(""), value: bytes.reverse().join(""),
}, this.updateField); },
} this.updateField,
);
};
generateRandom = () => { generateRandom = () => {
let cryptoObj = window.crypto || window.Crypto; let cryptoObj = window.crypto || window.Crypto;
let b = new Uint8Array(8); let b = new Uint8Array(8);
cryptoObj.getRandomValues(b); cryptoObj.getRandomValues(b);
let key = Buffer.from(b).toString('hex'); let key = Buffer.from(b).toString("hex");
this.setState({ this.setState(
{
value: key, value: key,
}, this.updateField); },
} this.updateField,
);
};
render() { render() {
const addon = ( const addon = (
@ -101,22 +109,33 @@ class EuiInput extends Component<IProps, IState> {
<Select.Option value="msb">MSB</Select.Option> <Select.Option value="msb">MSB</Select.Option>
<Select.Option value="lsb">LSB</Select.Option> <Select.Option value="lsb">LSB</Select.Option>
</Select> </Select>
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button> <Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
<ReloadOutlined />
</Button>
</Space> </Space>
); );
return ( return (
<Form.Item <Form.Item
rules={[{ rules={[
{
required: this.props.required, required: this.props.required,
message: `Please enter a valid ${this.props.label}`, message: `Please enter a valid ${this.props.label}`,
pattern: new RegExp(/[A-Fa-f0-9]{16}/g), pattern: new RegExp(/[A-Fa-f0-9]{16}/g),
}]} },
]}
label={this.props.label} label={this.props.label}
name={this.props.name} name={this.props.name}
> >
<Input hidden /> <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> </Form.Item>
); );
} }

View File

@ -13,27 +13,20 @@ import {
import InternalStore from "../stores/InternalStore"; import InternalStore from "../stores/InternalStore";
interface IProps { interface IProps {
user: User; user: User;
} }
interface IState { interface IState {
searchResult?: GlobalSearchResponse; searchResult?: GlobalSearchResponse;
settings?: SettingsResponse, settings?: SettingsResponse;
} }
const renderTitle = (title: string) => ( const renderTitle = (title: string) => <span>{title}</span>;
<span>
{title}
</span>
);
const renderItem = (title: string, url: string) => ({ const renderItem = (title: string, url: string) => ({
value: title, value: title,
label: ( label: <Link to={url}>{title}</Link>,
<Link to={url}>{title}</Link>
),
}); });
class Header extends Component<IProps, IState> { class Header extends Component<IProps, IState> {
@ -65,7 +58,7 @@ class Header extends Component<IProps, IState> {
searchResult: resp, searchResult: resp,
}); });
}); });
} };
render() { render() {
if (this.state.settings === undefined) { if (this.state.settings === undefined) {
@ -76,9 +69,11 @@ class Header extends Component<IProps, IState> {
const menu = ( const menu = (
<Menu> <Menu>
{!oidcEnabled && <Menu.Item> {!oidcEnabled && (
<Menu.Item>
<Link to={`/users/${this.props.user.getId()}/password`}>Change password</Link> <Link to={`/users/${this.props.user.getId()}/password`}>Change password</Link>
</Menu.Item>} </Menu.Item>
)}
<Menu.Item> <Menu.Item>
<Link to="/login">Logout</Link> <Link to="/login">Logout</Link>
</Menu.Item> </Menu.Item>
@ -90,19 +85,19 @@ class Header extends Component<IProps, IState> {
options: any[]; options: any[];
}[] = [ }[] = [
{ {
label: renderTitle('Tenants'), label: renderTitle("Tenants"),
options: [], options: [],
}, },
{ {
label: renderTitle('Gateways'), label: renderTitle("Gateways"),
options: [], options: [],
}, },
{ {
label: renderTitle('Applications'), label: renderTitle("Applications"),
options: [], options: [],
}, },
{ {
label: renderTitle('Devices'), label: renderTitle("Devices"),
options: [], options: [],
}, },
]; ];
@ -110,19 +105,31 @@ class Header extends Component<IProps, IState> {
if (this.state.searchResult !== undefined) { if (this.state.searchResult !== undefined) {
for (const res of this.state.searchResult.getResultList()) { for (const res of this.state.searchResult.getResultList()) {
if (res.getKind() === "tenant") { if (res.getKind() === "tenant") {
options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`)) options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`));
} }
if (res.getKind() === "gateway") { 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") { 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") { 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()}`,
),
);
} }
} }
} }
@ -142,11 +149,15 @@ class Header extends Component<IProps, IState> {
</AutoComplete> </AutoComplete>
</div> </div>
<div className="help"> <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>
<div className="user"> <div className="user">
<Dropdown overlay={menu} placement="bottomRight" trigger={['click']}> <Dropdown overlay={menu} placement="bottomRight" trigger={["click"]}>
<Button type="primary" icon={<UserOutlined />}>{this.props.user.getEmail()} <DownOutlined /></Button> <Button type="primary" icon={<UserOutlined />}>
{this.props.user.getEmail()} <DownOutlined />
</Button>
</Dropdown> </Dropdown>
</div> </div>
</div> </div>

View File

@ -3,20 +3,17 @@ import React, { Component } from "react";
import { color } from "chart.js/helpers"; import { color } from "chart.js/helpers";
import { Chart } from "react-chartjs-2"; import { Chart } from "react-chartjs-2";
interface HeatmapData { interface HeatmapData {
x: string; x: string;
y: Array<[string, number]>; y: Array<[string, number]>;
} }
interface IProps { interface IProps {
data: HeatmapData[]; data: HeatmapData[];
fromColor: string; fromColor: string;
toColor: string; toColor: string;
} }
class Heatmap extends Component<IProps> { class Heatmap extends Component<IProps> {
render() { render() {
if (this.props.data.length === 0) { if (this.props.data.length === 0) {
@ -45,14 +42,18 @@ class Heatmap extends Component<IProps> {
fromColor: this.props.fromColor.match(/\d+/g)!.map(Number), fromColor: this.props.fromColor.match(/\d+/g)!.map(Number),
toColor: this.props.toColor.match(/\d+/g)!.map(Number), toColor: this.props.toColor.match(/\d+/g)!.map(Number),
backgroundColor: (ctx: any): string => { backgroundColor: (ctx: any): string => {
if (ctx.dataset === undefined || ctx.dataset.data === undefined || ctx.dataset.data[ctx.dataIndex] === undefined) { if (
return color('white').rgbString(); 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 value = ctx.dataset.data[ctx.dataIndex].v;
const steps = ctx.dataset.maxValue - ctx.dataset.minValue + 1; const steps = ctx.dataset.maxValue - ctx.dataset.minValue + 1;
const step = value - ctx.dataset.minValue; 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(); let result: [number, number, number] = ctx.dataset.fromColor.slice();
for (var i = 0; i < 3; i++) { for (var i = 0; i < 3; i++) {
@ -94,7 +95,6 @@ class Heatmap extends Component<IProps> {
grid: { grid: {
display: false, display: false,
}, },
}, },
}, },
plugins: { plugins: {
@ -102,11 +102,11 @@ class Heatmap extends Component<IProps> {
tooltip: { tooltip: {
callbacks: { callbacks: {
title: () => { title: () => {
return ''; return "";
}, },
label: (ctx: any) => { label: (ctx: any) => {
const v = ctx.dataset.data[ctx.dataIndex].v; const v = ctx.dataset.data[ctx.dataIndex].v;
return 'Count: ' + v; return "Count: " + v;
}, },
}, },
}, },
@ -136,9 +136,7 @@ class Heatmap extends Component<IProps> {
} }
} }
return( return <Chart type="matrix" data={data} options={options} />;
<Chart type="matrix" data={data} options={options} />
);
} }
} }

View File

@ -3,11 +3,10 @@ import moment from "moment";
import JSONTreeOriginal from "react-json-tree"; import JSONTreeOriginal from "react-json-tree";
import { Tag, Drawer, Button, Table, Spin } from "antd"; 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"; import { LogItem } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
interface IProps { interface IProps {
logs: LogItem[]; logs: LogItem[];
} }
@ -15,8 +14,7 @@ interface IProps {
interface IState { interface IState {
drawerOpen: boolean; drawerOpen: boolean;
body: any; body: any;
}; }
class LogTable extends Component<IProps, IState> { class LogTable extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
@ -32,7 +30,7 @@ class LogTable extends Component<IProps, IState> {
this.setState({ this.setState({
drawerOpen: false, drawerOpen: false,
}); });
} };
onDrawerOpen = (body: any) => { onDrawerOpen = (body: any) => {
return () => { return () => {
@ -41,44 +39,56 @@ class LogTable extends Component<IProps, IState> {
drawerOpen: true, drawerOpen: true,
}); });
}; };
} };
render() { render() {
let items = this.props.logs.map((l, i) => l.toObject()); let items = this.props.logs.map((l, i) => l.toObject());
let body = JSON.parse(this.state.body); let body = JSON.parse(this.state.body);
const theme = { const theme = {
scheme: 'google', scheme: "google",
author: 'seth wright (http://sethawright.com)', author: "seth wright (http://sethawright.com)",
base00: '#000000', base00: "#000000",
base01: '#282a2e', base01: "#282a2e",
base02: '#373b41', base02: "#373b41",
base03: '#969896', base03: "#969896",
base04: '#b4b7b4', base04: "#b4b7b4",
base05: '#c5c8c6', base05: "#c5c8c6",
base06: '#e0e0e0', base06: "#e0e0e0",
base07: '#ffffff', base07: "#ffffff",
base08: '#CC342B', base08: "#CC342B",
base09: '#F96A38', base09: "#F96A38",
base0A: '#FBA922', base0A: "#FBA922",
base0B: '#198844', base0B: "#198844",
base0C: '#3971ED', base0C: "#3971ED",
base0D: '#3971ED', base0D: "#3971ED",
base0E: '#A36AC7', base0E: "#A36AC7",
base0F: '#3971ED', base0F: "#3971ED",
}; };
return ( return (
<div> <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 <JSONTreeOriginal
data={body} data={body}
theme={theme} theme={theme}
hideRoot={true} hideRoot={true}
shouldExpandNode={() => {return true}} shouldExpandNode={() => {
return true;
}}
/> />
</Drawer> </Drawer>
{items.length !== 0 && <div className="spinner"><Spin /></div>} {items.length !== 0 && (
<div className="spinner">
<Spin />
</div>
)}
<Table <Table
showHeader={false} showHeader={false}
loading={items.length === 0} loading={items.length === 0}
@ -101,15 +111,32 @@ class LogTable extends Component<IProps, IState> {
dataIndex: "description", dataIndex: "description",
key: "description", key: "description",
width: 200, 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", title: "Properties",
dataIndex: "properties", dataIndex: "properties",
key: "properties", key: "properties",
render: (text, obj) => obj.propertiesMap.map((p, i) => { render: (text, obj) =>
obj.propertiesMap.map((p, i) => {
if (p[1] !== "") { if (p[1] !== "") {
return <Tag><pre>{p[0]}: {p[1]}</pre></Tag> return (
<Tag>
<pre>
{p[0]}: {p[1]}
</pre>
</Tag>
);
} }
return null; return null;

View File

@ -3,8 +3,7 @@ import React, { Component } from "react";
import L, { LatLngTuple, FitBoundsOptions } from "leaflet"; import L, { LatLngTuple, FitBoundsOptions } from "leaflet";
import "leaflet.awesome-markers"; import "leaflet.awesome-markers";
import { MarkerProps as LMarkerProps } from "react-leaflet"; import { MarkerProps as LMarkerProps } from "react-leaflet";
import { MapContainer, Marker as LMarker, TileLayer } from 'react-leaflet'; import { MapContainer, Marker as LMarker, TileLayer } from "react-leaflet";
interface IProps { interface IProps {
height: number; height: number;
@ -17,7 +16,6 @@ interface IState {
map?: L.Map; map?: L.Map;
} }
class Map extends Component<IProps, IState> { class Map extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -25,9 +23,11 @@ class Map extends Component<IProps, IState> {
} }
setMap = (map: L.Map) => { setMap = (map: L.Map) => {
this.setState({ this.setState(
{
map: map, map: map,
}, () => { },
() => {
// This is needed as setMap is called after the map has been created. // 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 // 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. // the map with the new center because setMap hasn't been called yet.
@ -39,8 +39,9 @@ class Map extends Component<IProps, IState> {
if (this.props.bounds !== undefined) { if (this.props.bounds !== undefined) {
map.fitBounds(this.props.bounds, this.props.boundsOptions); map.fitBounds(this.props.bounds, this.props.boundsOptions);
} }
}); },
} );
};
componentDidUpdate(oldProps: IProps) { componentDidUpdate(oldProps: IProps) {
if (this.props === oldProps) { if (this.props === oldProps) {
@ -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 { interface MarkerProps extends LMarkerProps {
position: [number, number]; position: [number, number];
@ -102,9 +113,10 @@ export class Marker extends Component<MarkerProps> {
markerColor: color, markerColor: color,
}); });
return ( return (
<LMarker icon={iconMarker} position={position} {...otherProps}>{this.props.children}</LMarker> <LMarker icon={iconMarker} position={position} {...otherProps}>
{this.props.children}
</LMarker>
); );
} }
} }

View File

@ -2,9 +2,22 @@ import React, { Component } from "react";
import { withRouter, RouteComponentProps, Link } from "react-router-dom"; import { withRouter, RouteComponentProps, Link } from "react-router-dom";
import { Menu, MenuProps } from "antd"; 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 Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "../components/Autocomplete";
import TenantStore from "../stores/TenantStore"; import TenantStore from "../stores/TenantStore";
@ -48,7 +61,7 @@ class SideMenu extends Component<RouteComponentProps, IState> {
this.setState({ this.setState({
tenantId: SessionStore.getTenantId(), tenantId: SessionStore.getTenantId(),
}); });
} };
getTenantOptions = (search: string, fn: OptionsCallbackFunc) => { getTenantOptions = (search: string, fn: OptionsCallbackFunc) => {
let req = new ListTenantsRequest(); let req = new ListTenantsRequest();
@ -56,10 +69,12 @@ class SideMenu extends Component<RouteComponentProps, IState> {
req.setLimit(10); req.setLimit(10);
TenantStore.list(req, (resp: ListTenantsResponse) => { 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); fn(options);
}); });
} };
getTenantOption = (id: string, fn: OptionCallbackFunc) => { getTenantOption = (id: string, fn: OptionCallbackFunc) => {
TenantStore.get(id, (resp: GetTenantResponse) => { TenantStore.get(id, (resp: GetTenantResponse) => {
@ -68,19 +83,19 @@ class SideMenu extends Component<RouteComponentProps, IState> {
fn({ label: tenant.getName(), value: tenant.getId() }); fn({ label: tenant.getName(), value: tenant.getId() });
} }
}); });
} };
onTenantSelect = (value: string) => { onTenantSelect = (value: string) => {
SessionStore.setTenantId(value); SessionStore.setTenantId(value);
this.props.history.push(`/tenants/${value}`); this.props.history.push(`/tenants/${value}`);
} };
parseLocation = () => { parseLocation = () => {
const path = this.props.history.location.pathname; const path = this.props.history.location.pathname;
const tenantRe = /\/tenants\/([\w-]{36})/g; const tenantRe = /\/tenants\/([\w-]{36})/g;
const match = tenantRe.exec(path); const match = tenantRe.exec(path);
if (match !== null && (this.state.tenantId !== match[1])) { if (match !== null && this.state.tenantId !== match[1]) {
SessionStore.setTenantId(match[1]); SessionStore.setTenantId(match[1]);
} }
@ -104,7 +119,6 @@ class SideMenu extends Component<RouteComponentProps, IState> {
this.setState({ selectedKey: "ns-api-keys" }); this.setState({ selectedKey: "ns-api-keys" });
} }
// tenant dashboard // tenant dashboard
if (/\/tenants\/[\w-]{36}/g.exec(path)) { if (/\/tenants\/[\w-]{36}/g.exec(path)) {
this.setState({ selectedKey: "tenant-dashboard" }); this.setState({ selectedKey: "tenant-dashboard" });
@ -134,7 +148,7 @@ class SideMenu extends Component<RouteComponentProps, IState> {
if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) { if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) {
this.setState({ selectedKey: "tenant-applications" }); this.setState({ selectedKey: "tenant-applications" });
} }
} };
render() { render() {
const tenantId = this.state.tenantId; const tenantId = this.state.tenantId;

View File

@ -1,2 +1,2 @@
import { createHashHistory } from 'history'; import { createHashHistory } from "history";
export default createHashHistory(); export default createHashHistory();

View File

@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
// import { Chart, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, Title } from "chart.js"; // import { Chart, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, Title } from "chart.js";
import { Chart, registerables } from "chart.js"; import { Chart, registerables } from "chart.js";
import { MatrixElement, MatrixController } from 'chartjs-chart-matrix'; import { MatrixElement, MatrixController } from "chartjs-chart-matrix";
import "chartjs-adapter-moment"; import "chartjs-adapter-moment";
import App from "./App"; import App from "./App";
@ -23,7 +23,7 @@ ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>,
document.getElementById("root") document.getElementById("root"),
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function

View File

@ -1,8 +1,8 @@
import { ReportHandler } from 'web-vitals'; import { ReportHandler } from "web-vitals";
const reportWebVitals = (onPerfEntry?: ReportHandler) => { const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) { 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); getCLS(onPerfEntry);
getFID(onPerfEntry); getFID(onPerfEntry);
getFCP(onPerfEntry); getFCP(onPerfEntry);

View File

@ -2,4 +2,4 @@
// allows you to do things like: // allows you to do things like:
// expect(element).toHaveTextContent(/react/i) // expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom // learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom'; import "@testing-library/jest-dom";

View File

@ -64,7 +64,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class ApplicationStore extends EventEmitter { class ApplicationStore extends EventEmitter {
client: ApplicationServiceClient; client: ApplicationServiceClient;
@ -87,7 +86,7 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
get = (req: GetApplicationRequest, callbackFunc: (resp: GetApplicationResponse) => void) => { get = (req: GetApplicationRequest, callbackFunc: (resp: GetApplicationResponse) => void) => {
this.client.get(req, SessionStore.getMetadata(), (err, resp) => { this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
@ -98,10 +97,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
update = (req: UpdateApplicationRequest, callbackFunc: () => void) => { update = (req: UpdateApplicationRequest, callbackFunc: () => void) => {
this.client.update(req, SessionStore.getMetadata(), (err) => { this.client.update(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -114,10 +113,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
delete = (req: DeleteApplicationRequest, callbackFunc: () => void) => { delete = (req: DeleteApplicationRequest, callbackFunc: () => void) => {
this.client.delete(req, SessionStore.getMetadata(), (err) => { this.client.delete(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -130,7 +129,7 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
list = (req: ListApplicationsRequest, callbackFunc: (resp: ListApplicationsResponse) => void) => { list = (req: ListApplicationsRequest, callbackFunc: (resp: ListApplicationsResponse) => void) => {
this.client.list(req, SessionStore.getMetadata(), (err, resp) => { this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
@ -141,7 +140,7 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
listIntegrations = (req: ListIntegrationsRequest, callbackFunc: (resp: ListIntegrationsResponse) => void) => { listIntegrations = (req: ListIntegrationsRequest, callbackFunc: (resp: ListIntegrationsResponse) => void) => {
this.client.listIntegrations(req, SessionStore.getMetadata(), (err, resp) => { this.client.listIntegrations(req, SessionStore.getMetadata(), (err, resp) => {
@ -151,11 +150,11 @@ class ApplicationStore extends EventEmitter {
} }
callbackFunc(resp); callbackFunc(resp);
}) });
} };
createHttpIntegration = (req: CreateHttpIntegrationRequest, callbackFunc: () => void) => { createHttpIntegration = (req: CreateHttpIntegrationRequest, callbackFunc: () => void) => {
this.client.createHttpIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createHttpIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -168,7 +167,7 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getHttpIntegration = (req: GetHttpIntegrationRequest, callbackFunc: (resp: GetHttpIntegrationResponse) => void) => { getHttpIntegration = (req: GetHttpIntegrationRequest, callbackFunc: (resp: GetHttpIntegrationResponse) => void) => {
this.client.getHttpIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getHttpIntegration(req, SessionStore.getMetadata(), (err, resp) => {
@ -179,10 +178,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateHttpIntegration = (req: UpdateHttpIntegrationRequest, callbackFunc: () => void) => { updateHttpIntegration = (req: UpdateHttpIntegrationRequest, callbackFunc: () => void) => {
this.client.updateHttpIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateHttpIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -195,10 +194,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteHttpIntegration = (req: DeleteHttpIntegrationRequest, callbackFunc: () => void) => { deleteHttpIntegration = (req: DeleteHttpIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteHttpIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteHttpIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -212,10 +211,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createAwsSnsIntegration = (req: CreateAwsSnsIntegrationRequest, callbackFunc: () => void) => { createAwsSnsIntegration = (req: CreateAwsSnsIntegrationRequest, callbackFunc: () => void) => {
this.client.createAwsSnsIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createAwsSnsIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -228,9 +227,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getAwsSnsIntegration = (req: GetAwsSnsIntegrationRequest, callbackFunc: (resp: GetAwsSnsIntegrationResponse) => void) => { getAwsSnsIntegration = (
req: GetAwsSnsIntegrationRequest,
callbackFunc: (resp: GetAwsSnsIntegrationResponse) => void,
) => {
this.client.getAwsSnsIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getAwsSnsIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -239,10 +241,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateAwsSnsIntegration = (req: UpdateAwsSnsIntegrationRequest, callbackFunc: () => void) => { updateAwsSnsIntegration = (req: UpdateAwsSnsIntegrationRequest, callbackFunc: () => void) => {
this.client.updateAwsSnsIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateAwsSnsIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -255,10 +257,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteAwsSnsIntegration = (req: DeleteAwsSnsIntegrationRequest, callbackFunc: () => void) => { deleteAwsSnsIntegration = (req: DeleteAwsSnsIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteAwsSnsIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteAwsSnsIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -272,10 +274,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createAzureServiceBusIntegration = (req: CreateAzureServiceBusIntegrationRequest, callbackFunc: () => void) => { createAzureServiceBusIntegration = (req: CreateAzureServiceBusIntegrationRequest, callbackFunc: () => void) => {
this.client.createAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createAzureServiceBusIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -288,9 +290,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getAzureServiceBusIntegration = (req: GetAzureServiceBusIntegrationRequest, callbackFunc: (resp: GetAzureServiceBusIntegrationResponse) => void) => { getAzureServiceBusIntegration = (
req: GetAzureServiceBusIntegrationRequest,
callbackFunc: (resp: GetAzureServiceBusIntegrationResponse) => void,
) => {
this.client.getAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -299,10 +304,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateAzureServiceBusIntegration = (req: UpdateAzureServiceBusIntegrationRequest, callbackFunc: () => void) => { updateAzureServiceBusIntegration = (req: UpdateAzureServiceBusIntegrationRequest, callbackFunc: () => void) => {
this.client.updateAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateAzureServiceBusIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -315,10 +320,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteAzureServiceBusIntegration = (req: DeleteAzureServiceBusIntegrationRequest, callbackFunc: () => void) => { deleteAzureServiceBusIntegration = (req: DeleteAzureServiceBusIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteAzureServiceBusIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteAzureServiceBusIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -332,10 +337,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createGcpPubSubIntegration = (req: CreateGcpPubSubIntegrationRequest, callbackFunc: () => void) => { createGcpPubSubIntegration = (req: CreateGcpPubSubIntegrationRequest, callbackFunc: () => void) => {
this.client.createGcpPubSubIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createGcpPubSubIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -348,9 +353,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getGcpPubSubIntegration = (req: GetGcpPubSubIntegrationRequest, callbackFunc: (resp: GetGcpPubSubIntegrationResponse) => void) => { getGcpPubSubIntegration = (
req: GetGcpPubSubIntegrationRequest,
callbackFunc: (resp: GetGcpPubSubIntegrationResponse) => void,
) => {
this.client.getGcpPubSubIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getGcpPubSubIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -359,10 +367,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateGcpPubSubIntegration = (req: UpdateGcpPubSubIntegrationRequest, callbackFunc: () => void) => { updateGcpPubSubIntegration = (req: UpdateGcpPubSubIntegrationRequest, callbackFunc: () => void) => {
this.client.updateGcpPubSubIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateGcpPubSubIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -375,10 +383,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteGcpPubSubIntegration = (req: DeleteGcpPubSubIntegrationRequest, callbackFunc: () => void) => { deleteGcpPubSubIntegration = (req: DeleteGcpPubSubIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteGcpPubSubIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteGcpPubSubIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -392,10 +400,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createInfluxDbIntegration = (req: CreateInfluxDbIntegrationRequest, callbackFunc: () => void) => { createInfluxDbIntegration = (req: CreateInfluxDbIntegrationRequest, callbackFunc: () => void) => {
this.client.createInfluxDbIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createInfluxDbIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -408,9 +416,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getInfluxDbIntegration = (req: GetInfluxDbIntegrationRequest, callbackFunc: (resp: GetInfluxDbIntegrationResponse) => void) => { getInfluxDbIntegration = (
req: GetInfluxDbIntegrationRequest,
callbackFunc: (resp: GetInfluxDbIntegrationResponse) => void,
) => {
this.client.getInfluxDbIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getInfluxDbIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -419,10 +430,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateInfluxDbIntegration = (req: UpdateInfluxDbIntegrationRequest, callbackFunc: () => void) => { updateInfluxDbIntegration = (req: UpdateInfluxDbIntegrationRequest, callbackFunc: () => void) => {
this.client.updateInfluxDbIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateInfluxDbIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -435,10 +446,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteInfluxDbIntegration = (req: DeleteInfluxDbIntegrationRequest, callbackFunc: () => void) => { deleteInfluxDbIntegration = (req: DeleteInfluxDbIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteInfluxDbIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteInfluxDbIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -452,10 +463,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createMyDevicesIntegration = (req: CreateMyDevicesIntegrationRequest, callbackFunc: () => void) => { createMyDevicesIntegration = (req: CreateMyDevicesIntegrationRequest, callbackFunc: () => void) => {
this.client.createMyDevicesIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createMyDevicesIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -468,9 +479,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getMyDevicesIntegration = (req: GetMyDevicesIntegrationRequest, callbackFunc: (resp: GetMyDevicesIntegrationResponse) => void) => { getMyDevicesIntegration = (
req: GetMyDevicesIntegrationRequest,
callbackFunc: (resp: GetMyDevicesIntegrationResponse) => void,
) => {
this.client.getMyDevicesIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getMyDevicesIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -479,10 +493,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateMyDevicesIntegration = (req: UpdateMyDevicesIntegrationRequest, callbackFunc: () => void) => { updateMyDevicesIntegration = (req: UpdateMyDevicesIntegrationRequest, callbackFunc: () => void) => {
this.client.updateMyDevicesIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateMyDevicesIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -495,10 +509,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteMyDevicesIntegration = (req: DeleteMyDevicesIntegrationRequest, callbackFunc: () => void) => { deleteMyDevicesIntegration = (req: DeleteMyDevicesIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteMyDevicesIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteMyDevicesIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -512,10 +526,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createPilotThingsIntegration = (req: CreatePilotThingsIntegrationRequest, callbackFunc: () => void) => { createPilotThingsIntegration = (req: CreatePilotThingsIntegrationRequest, callbackFunc: () => void) => {
this.client.createPilotThingsIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createPilotThingsIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -528,9 +542,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getPilotThingsIntegration = (req: GetPilotThingsIntegrationRequest, callbackFunc: (resp: GetPilotThingsIntegrationResponse) => void) => { getPilotThingsIntegration = (
req: GetPilotThingsIntegrationRequest,
callbackFunc: (resp: GetPilotThingsIntegrationResponse) => void,
) => {
this.client.getPilotThingsIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getPilotThingsIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -539,10 +556,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updatePilotThingsIntegration = (req: UpdatePilotThingsIntegrationRequest, callbackFunc: () => void) => { updatePilotThingsIntegration = (req: UpdatePilotThingsIntegrationRequest, callbackFunc: () => void) => {
this.client.updatePilotThingsIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updatePilotThingsIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -555,10 +572,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deletePilotThingsIntegration = (req: DeletePilotThingsIntegrationRequest, callbackFunc: () => void) => { deletePilotThingsIntegration = (req: DeletePilotThingsIntegrationRequest, callbackFunc: () => void) => {
this.client.deletePilotThingsIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deletePilotThingsIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -572,10 +589,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createLoraCloudIntegration = (req: CreateLoraCloudIntegrationRequest, callbackFunc: () => void) => { createLoraCloudIntegration = (req: CreateLoraCloudIntegrationRequest, callbackFunc: () => void) => {
this.client.createLoraCloudIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createLoraCloudIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -588,9 +605,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getLoraCloudIntegration = (req: GetLoraCloudIntegrationRequest, callbackFunc: (resp: GetLoraCloudIntegrationResponse) => void) => { getLoraCloudIntegration = (
req: GetLoraCloudIntegrationRequest,
callbackFunc: (resp: GetLoraCloudIntegrationResponse) => void,
) => {
this.client.getLoraCloudIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getLoraCloudIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -599,10 +619,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateLoraCloudIntegration = (req: UpdateLoraCloudIntegrationRequest, callbackFunc: () => void) => { updateLoraCloudIntegration = (req: UpdateLoraCloudIntegrationRequest, callbackFunc: () => void) => {
this.client.updateLoraCloudIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateLoraCloudIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -615,10 +635,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteLoraCloudIntegration = (req: DeleteLoraCloudIntegrationRequest, callbackFunc: () => void) => { deleteLoraCloudIntegration = (req: DeleteLoraCloudIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteLoraCloudIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteLoraCloudIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -632,10 +652,10 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
createThingsBoardIntegration = (req: CreateThingsBoardIntegrationRequest, callbackFunc: () => void) => { createThingsBoardIntegration = (req: CreateThingsBoardIntegrationRequest, callbackFunc: () => void) => {
this.client.createThingsBoardIntegration(req, SessionStore.getMetadata(), (err) => { this.client.createThingsBoardIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -648,9 +668,12 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getThingsBoardIntegration = (req: GetThingsBoardIntegrationRequest, callbackFunc: (resp: GetThingsBoardIntegrationResponse) => void) => { getThingsBoardIntegration = (
req: GetThingsBoardIntegrationRequest,
callbackFunc: (resp: GetThingsBoardIntegrationResponse) => void,
) => {
this.client.getThingsBoardIntegration(req, SessionStore.getMetadata(), (err, resp) => { this.client.getThingsBoardIntegration(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -659,10 +682,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateThingsBoardIntegration = (req: UpdateThingsBoardIntegrationRequest, callbackFunc: () => void) => { updateThingsBoardIntegration = (req: UpdateThingsBoardIntegrationRequest, callbackFunc: () => void) => {
this.client.updateThingsBoardIntegration(req, SessionStore.getMetadata(), (err) => { this.client.updateThingsBoardIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -675,10 +698,10 @@ class ApplicationStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteThingsBoardIntegration = (req: DeleteThingsBoardIntegrationRequest, callbackFunc: () => void) => { deleteThingsBoardIntegration = (req: DeleteThingsBoardIntegrationRequest, callbackFunc: () => void) => {
this.client.deleteThingsBoardIntegration(req, SessionStore.getMetadata(), (err) => { this.client.deleteThingsBoardIntegration(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -692,9 +715,12 @@ class ApplicationStore extends EventEmitter {
this.emit("integration.delete"); this.emit("integration.delete");
callbackFunc(); callbackFunc();
}); });
} };
generateMqttIntegrationClientCertificate = (req: GenerateMqttIntegrationClientCertificateRequest, callbackFunc: (resp: GenerateMqttIntegrationClientCertificateResponse) => void) => { generateMqttIntegrationClientCertificate = (
req: GenerateMqttIntegrationClientCertificateRequest,
callbackFunc: (resp: GenerateMqttIntegrationClientCertificateResponse) => void,
) => {
this.client.generateMqttIntegrationClientCertificate(req, SessionStore.getMetadata(), (err, resp) => { this.client.generateMqttIntegrationClientCertificate(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -703,7 +729,7 @@ class ApplicationStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
} }
const applicationStore = new ApplicationStore(); const applicationStore = new ApplicationStore();

View File

@ -17,7 +17,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class DeviceProfileStore extends EventEmitter { class DeviceProfileStore extends EventEmitter {
client: DeviceProfileServiceClient; client: DeviceProfileServiceClient;
@ -40,7 +39,7 @@ class DeviceProfileStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
get = (req: GetDeviceProfileRequest, callbackFunc: (resp: GetDeviceProfileResponse) => void) => { get = (req: GetDeviceProfileRequest, callbackFunc: (resp: GetDeviceProfileResponse) => void) => {
this.client.get(req, SessionStore.getMetadata(), (err, resp) => { this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
@ -51,10 +50,10 @@ class DeviceProfileStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
update = (req: UpdateDeviceProfileRequest, callbackFunc: () => void) => { update = (req: UpdateDeviceProfileRequest, callbackFunc: () => void) => {
this.client.update(req, SessionStore.getMetadata(), (err) => { this.client.update(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -67,10 +66,10 @@ class DeviceProfileStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
delete = (req: DeleteDeviceProfileRequest, callbackFunc: () => void) => { delete = (req: DeleteDeviceProfileRequest, callbackFunc: () => void) => {
this.client.delete(req, SessionStore.getMetadata(), (err) => { this.client.delete(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -83,7 +82,7 @@ class DeviceProfileStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
list = (req: ListDeviceProfilesRequest, callbackFunc: (resp: ListDeviceProfilesResponse) => void) => { list = (req: ListDeviceProfilesRequest, callbackFunc: (resp: ListDeviceProfilesResponse) => void) => {
this.client.list(req, SessionStore.getMetadata(), (err, resp) => { this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
@ -93,8 +92,8 @@ class DeviceProfileStore extends EventEmitter {
} }
callbackFunc(resp); callbackFunc(resp);
}) });
} };
listAdrAlgorithms = (callbackFunc: (resp: ListDeviceProfileAdrAlgorithmsResponse) => void) => { listAdrAlgorithms = (callbackFunc: (resp: ListDeviceProfileAdrAlgorithmsResponse) => void) => {
this.client.listAdrAlgorithms(new google_protobuf_empty_pb.Empty(), SessionStore.getMetadata(), (err, resp) => { this.client.listAdrAlgorithms(new google_protobuf_empty_pb.Empty(), SessionStore.getMetadata(), (err, resp) => {
@ -105,7 +104,7 @@ class DeviceProfileStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
} }
const deviceProfileStore = new DeviceProfileStore(); const deviceProfileStore = new DeviceProfileStore();

View File

@ -33,7 +33,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class DeviceStore extends EventEmitter { class DeviceStore extends EventEmitter {
client: DeviceServiceClient; client: DeviceServiceClient;
@ -43,7 +42,7 @@ class DeviceStore extends EventEmitter {
} }
create = (req: CreateDeviceRequest, callbackFunc: () => void) => { create = (req: CreateDeviceRequest, callbackFunc: () => void) => {
this.client.create(req, SessionStore.getMetadata(), (err) => { this.client.create(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -56,7 +55,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
get = (req: GetDeviceRequest, callbackFunc: (resp: GetDeviceResponse) => void) => { get = (req: GetDeviceRequest, callbackFunc: (resp: GetDeviceResponse) => void) => {
this.client.get(req, SessionStore.getMetadata(), (err, resp) => { this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
@ -67,10 +66,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
update = (req: UpdateDeviceRequest, callbackFunc: () => void) => { update = (req: UpdateDeviceRequest, callbackFunc: () => void) => {
this.client.update(req, SessionStore.getMetadata(), (err) => { this.client.update(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -83,10 +82,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
delete = (req: DeleteDeviceRequest, callbackFunc: () => void) => { delete = (req: DeleteDeviceRequest, callbackFunc: () => void) => {
this.client.delete(req, SessionStore.getMetadata(), (err) => { this.client.delete(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -99,7 +98,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
list = (req: ListDevicesRequest, callbackFunc: (resp: ListDevicesResponse) => void) => { list = (req: ListDevicesRequest, callbackFunc: (resp: ListDevicesResponse) => void) => {
this.client.list(req, SessionStore.getMetadata(), (err, resp) => { this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
@ -110,10 +109,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
createKeys = (req: CreateDeviceKeysRequest, callbackFunc: () => void) => { createKeys = (req: CreateDeviceKeysRequest, callbackFunc: () => void) => {
this.client.createKeys(req, SessionStore.getMetadata(), (err) => { this.client.createKeys(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -126,7 +125,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getKeys = (req: GetDeviceKeysRequest, callbackFunc: (resp?: GetDeviceKeysResponse) => void) => { getKeys = (req: GetDeviceKeysRequest, callbackFunc: (resp?: GetDeviceKeysResponse) => void) => {
this.client.getKeys(req, SessionStore.getMetadata(), (err, resp) => { this.client.getKeys(req, SessionStore.getMetadata(), (err, resp) => {
@ -139,10 +138,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateKeys = (req: UpdateDeviceKeysRequest, callbackFunc: () => void) => { updateKeys = (req: UpdateDeviceKeysRequest, callbackFunc: () => void) => {
this.client.updateKeys(req, SessionStore.getMetadata(), (err) => { this.client.updateKeys(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -155,10 +154,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteKeys = (req: DeleteDeviceKeysRequest, callbackFunc: () => void) => { deleteKeys = (req: DeleteDeviceKeysRequest, callbackFunc: () => void) => {
this.client.deleteKeys(req, SessionStore.getMetadata(), (err) => { this.client.deleteKeys(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -171,7 +170,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getStats = (req: GetDeviceStatsRequest, callbackFunc: (resp: GetDeviceStatsResponse) => void) => { getStats = (req: GetDeviceStatsRequest, callbackFunc: (resp: GetDeviceStatsResponse) => void) => {
this.client.getStats(req, SessionStore.getMetadata(), (err, resp) => { this.client.getStats(req, SessionStore.getMetadata(), (err, resp) => {
@ -182,7 +181,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
enqueue = (req: EnqueueDeviceQueueItemRequest, callbackFunc: (resp: EnqueueDeviceQueueItemResponse) => void) => { enqueue = (req: EnqueueDeviceQueueItemRequest, callbackFunc: (resp: EnqueueDeviceQueueItemResponse) => void) => {
this.client.enqueue(req, SessionStore.getMetadata(), (err, resp) => { this.client.enqueue(req, SessionStore.getMetadata(), (err, resp) => {
@ -193,10 +192,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
flushQueue = (req: FlushDeviceQueueRequest, callbackFunc: () => void) => { flushQueue = (req: FlushDeviceQueueRequest, callbackFunc: () => void) => {
this.client.flushQueue(req, SessionStore.getMetadata(), (err) => { this.client.flushQueue(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -204,10 +203,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
flushDevNonces = (req: FlushDevNoncesRequest, callbackFunc: () => void) => { flushDevNonces = (req: FlushDevNoncesRequest, callbackFunc: () => void) => {
this.client.flushDevNonces(req, SessionStore.getMetadata(), (err) => { this.client.flushDevNonces(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -220,7 +219,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getQueue = (req: GetDeviceQueueItemsRequest, callbackFunc: (resp: GetDeviceQueueItemsResponse) => void) => { getQueue = (req: GetDeviceQueueItemsRequest, callbackFunc: (resp: GetDeviceQueueItemsResponse) => void) => {
this.client.getQueue(req, SessionStore.getMetadata(), (err, resp) => { this.client.getQueue(req, SessionStore.getMetadata(), (err, resp) => {
@ -231,10 +230,10 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
activate = (req: ActivateDeviceRequest, callbackFunc: () => void) => { activate = (req: ActivateDeviceRequest, callbackFunc: () => void) => {
this.client.activate(req, SessionStore.getMetadata(), (err) => { this.client.activate(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -247,7 +246,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getActivation = (req: GetDeviceActivationRequest, callbackFunc: (resp: GetDeviceActivationResponse) => void) => { getActivation = (req: GetDeviceActivationRequest, callbackFunc: (resp: GetDeviceActivationResponse) => void) => {
this.client.getActivation(req, SessionStore.getMetadata(), (err, resp) => { this.client.getActivation(req, SessionStore.getMetadata(), (err, resp) => {
@ -258,7 +257,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
getRandomDevAddr = (req: GetRandomDevAddrRequest, callbackFunc: (resp: GetRandomDevAddrResponse) => void) => { getRandomDevAddr = (req: GetRandomDevAddrRequest, callbackFunc: (resp: GetRandomDevAddrResponse) => void) => {
this.client.getRandomDevAddr(req, SessionStore.getMetadata(), (err, resp) => { this.client.getRandomDevAddr(req, SessionStore.getMetadata(), (err, resp) => {
@ -269,7 +268,7 @@ class DeviceStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
} }
const deviceStore = new DeviceStore(); const deviceStore = new DeviceStore();

View File

@ -18,7 +18,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class GatewayStore extends EventEmitter { class GatewayStore extends EventEmitter {
client: GatewayServiceClient; client: GatewayServiceClient;
@ -28,7 +27,7 @@ class GatewayStore extends EventEmitter {
} }
create = (req: CreateGatewayRequest, callbackFunc: () => void) => { create = (req: CreateGatewayRequest, callbackFunc: () => void) => {
this.client.create(req, SessionStore.getMetadata(), (err) => { this.client.create(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -41,7 +40,7 @@ class GatewayStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
get = (req: GetGatewayRequest, callbackFunc: (resp: GetGatewayResponse) => void) => { get = (req: GetGatewayRequest, callbackFunc: (resp: GetGatewayResponse) => void) => {
this.client.get(req, SessionStore.getMetadata(), (err, resp) => { this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
@ -52,10 +51,10 @@ class GatewayStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
update = (req: UpdateGatewayRequest, callbackFunc: () => void) => { update = (req: UpdateGatewayRequest, callbackFunc: () => void) => {
this.client.update(req, SessionStore.getMetadata(), (err) => { this.client.update(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -68,10 +67,10 @@ class GatewayStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
delete = (req: DeleteGatewayRequest, callbackFunc: () => void) => { delete = (req: DeleteGatewayRequest, callbackFunc: () => void) => {
this.client.delete(req, SessionStore.getMetadata(), (err) => { this.client.delete(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -84,7 +83,7 @@ class GatewayStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
list = (req: ListGatewaysRequest, callbackFunc: (resp: ListGatewaysResponse) => void) => { list = (req: ListGatewaysRequest, callbackFunc: (resp: ListGatewaysResponse) => void) => {
this.client.list(req, SessionStore.getMetadata(), (err, resp) => { this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
@ -95,7 +94,7 @@ class GatewayStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
getStats = (req: GetGatewayStatsRequest, callbackFunc: (resp: GetGatewayStatsResponse) => void) => { getStats = (req: GetGatewayStatsRequest, callbackFunc: (resp: GetGatewayStatsResponse) => void) => {
this.client.getStats(req, SessionStore.getMetadata(), (err, resp) => { this.client.getStats(req, SessionStore.getMetadata(), (err, resp) => {
@ -106,9 +105,12 @@ class GatewayStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
generateClientCertificate = (req: GenerateGatewayClientCertificateRequest, callbackFunc: (resp: GenerateGatewayClientCertificateResponse) => void) => { generateClientCertificate = (
req: GenerateGatewayClientCertificateRequest,
callbackFunc: (resp: GenerateGatewayClientCertificateResponse) => void,
) => {
this.client.generateClientCertificate(req, SessionStore.getMetadata(), (err, resp) => { this.client.generateClientCertificate(req, SessionStore.getMetadata(), (err, resp) => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
@ -117,7 +119,7 @@ class GatewayStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
} }
const gatewayStore = new GatewayStore(); const gatewayStore = new GatewayStore();

View File

@ -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 google_protobuf_empty_pb from "google-protobuf/google/protobuf/empty_pb";
import { notification } from "antd"; import { notification } from "antd";
@ -26,7 +26,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class InternalStore extends EventEmitter { class InternalStore extends EventEmitter {
client: InternalServiceClient; client: InternalServiceClient;
@ -49,7 +48,7 @@ class InternalStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
deleteApiKey = (req: DeleteApiKeyRequest, callbackFunc: () => void) => { deleteApiKey = (req: DeleteApiKeyRequest, callbackFunc: () => void) => {
this.client.deleteApiKey(req, SessionStore.getMetadata(), (err, resp) => { this.client.deleteApiKey(req, SessionStore.getMetadata(), (err, resp) => {
@ -65,7 +64,7 @@ class InternalStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
listApiKeys = (req: ListApiKeysRequest, callbackFunc: (resp: ListApiKeysResponse) => void) => { listApiKeys = (req: ListApiKeysRequest, callbackFunc: (resp: ListApiKeysResponse) => void) => {
this.client.listApiKeys(req, SessionStore.getMetadata(), (err, resp) => { this.client.listApiKeys(req, SessionStore.getMetadata(), (err, resp) => {
@ -76,26 +75,25 @@ class InternalStore extends EventEmitter {
callbackFunc(resp); 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; var stream: grpcWeb.ClientReadableStream<LogItem> | undefined = undefined;
let setup = () => { let setup = () => {
console.log("Setting up gRPC stream"); console.log("Setting up gRPC stream");
stream = this.client.streamGatewayFrames(req, SessionStore.getMetadata()); stream = this.client.streamGatewayFrames(req, SessionStore.getMetadata());
stream = stream.on("data", (resp) => { stream = stream.on("data", resp => {
callbackFunc(resp); callbackFunc(resp);
}); });
stream = stream.on('end', function() { stream = stream.on("end", function () {
console.log("gRPC stream end, reconnecting"); console.log("gRPC stream end, reconnecting");
setTimeout(setup, 1000); setTimeout(setup, 1000);
}); });
}; };
setup(); setup();
return () => { return () => {
@ -103,26 +101,25 @@ class InternalStore extends EventEmitter {
stream.cancel(); 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; var stream: grpcWeb.ClientReadableStream<LogItem> | undefined = undefined;
let setup = () => { let setup = () => {
console.log("Setting up gRPC stream"); console.log("Setting up gRPC stream");
stream = this.client.streamDeviceFrames(req, SessionStore.getMetadata()); stream = this.client.streamDeviceFrames(req, SessionStore.getMetadata());
stream = stream.on("data", (resp) => { stream = stream.on("data", resp => {
callbackFunc(resp); callbackFunc(resp);
}); });
stream = stream.on('end', function() { stream = stream.on("end", function () {
console.log("gRPC stream end, reconnecting"); console.log("gRPC stream end, reconnecting");
setTimeout(setup, 1000); setTimeout(setup, 1000);
}); });
}; };
setup(); setup();
return () => { return () => {
@ -130,20 +127,20 @@ class InternalStore extends EventEmitter {
stream.cancel(); 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; var stream: grpcWeb.ClientReadableStream<LogItem> | undefined = undefined;
let setup = () => { let setup = () => {
console.log("Setting up gRPC stream"); console.log("Setting up gRPC stream");
stream = this.client.streamDeviceEvents(req, SessionStore.getMetadata()); stream = this.client.streamDeviceEvents(req, SessionStore.getMetadata());
stream = stream.on("data", (resp) => { stream = stream.on("data", resp => {
callbackFunc(resp); callbackFunc(resp);
}); });
stream = stream.on('end', function() { stream = stream.on("end", function () {
console.log("gRPC stream end, reconnecting"); console.log("gRPC stream end, reconnecting");
setTimeout(setup, 1000); setTimeout(setup, 1000);
}); });
@ -156,7 +153,7 @@ class InternalStore extends EventEmitter {
stream.cancel(); stream.cancel();
} }
}; };
} };
getGatewaysSummary = (req: GetGatewaysSummaryRequest, callbackFunc: (resp: GetGatewaysSummaryResponse) => void) => { getGatewaysSummary = (req: GetGatewaysSummaryRequest, callbackFunc: (resp: GetGatewaysSummaryResponse) => void) => {
this.client.getGatewaysSummary(req, SessionStore.getMetadata(), (err, resp) => { this.client.getGatewaysSummary(req, SessionStore.getMetadata(), (err, resp) => {
@ -167,7 +164,7 @@ class InternalStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
getDevicesSummary = (req: GetDevicesSummaryRequest, callbackFunc: (resp: GetDevicesSummaryResponse) => void) => { getDevicesSummary = (req: GetDevicesSummaryRequest, callbackFunc: (resp: GetDevicesSummaryResponse) => void) => {
this.client.getDevicesSummary(req, SessionStore.getMetadata(), (err, resp) => { this.client.getDevicesSummary(req, SessionStore.getMetadata(), (err, resp) => {
@ -178,7 +175,7 @@ class InternalStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
settings = (callbackFunc: (resp: SettingsResponse) => void) => { settings = (callbackFunc: (resp: SettingsResponse) => void) => {
this.client.settings(new google_protobuf_empty_pb.Empty(), {}, (err, resp) => { this.client.settings(new google_protobuf_empty_pb.Empty(), {}, (err, resp) => {
@ -189,7 +186,7 @@ class InternalStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
globalSearch = (req: GlobalSearchRequest, callbackFunc: (resp: GlobalSearchResponse) => void) => { globalSearch = (req: GlobalSearchRequest, callbackFunc: (resp: GlobalSearchResponse) => void) => {
this.client.globalSearch(req, SessionStore.getMetadata(), (err, resp) => { this.client.globalSearch(req, SessionStore.getMetadata(), (err, resp) => {
@ -200,7 +197,7 @@ class InternalStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
} }
const internalStore = new InternalStore(); const internalStore = new InternalStore();

View File

@ -11,17 +11,21 @@ class LocationStore extends EventEmitter {
return; return;
} }
navigator.geolocation.getCurrentPosition((p: GeolocationPosition) => { navigator.geolocation.getCurrentPosition(
(p: GeolocationPosition) => {
callbackFunc([p.coords.latitude, p.coords.longitude]); callbackFunc([p.coords.latitude, p.coords.longitude]);
}, (e: GeolocationPositionError) => { },
(e: GeolocationPositionError) => {
notification.error({ notification.error({
message: e.message, message: e.message,
duration: 3, duration: 3,
}); });
}, { },
{
timeout: 3000, timeout: 3000,
}); },
} );
};
} }
const locationStore = new LocationStore(); const locationStore = new LocationStore();

View File

@ -20,7 +20,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class MulticastGroupStore extends EventEmitter { class MulticastGroupStore extends EventEmitter {
client: MulticastGroupServiceClient; client: MulticastGroupServiceClient;
@ -43,7 +42,7 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
get = (req: GetMulticastGroupRequest, callbackFunc: (resp: GetMulticastGroupResponse) => void) => { get = (req: GetMulticastGroupRequest, callbackFunc: (resp: GetMulticastGroupResponse) => void) => {
this.client.get(req, SessionStore.getMetadata(), (err, resp) => { this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
@ -54,10 +53,10 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
update = (req: UpdateMulticastGroupRequest, callbackFunc: () => void) => { update = (req: UpdateMulticastGroupRequest, callbackFunc: () => void) => {
this.client.update(req, SessionStore.getMetadata(), (err) => { this.client.update(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -70,10 +69,10 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
delete = (req: DeleteMulticastGroupRequest, callbackFunc: () => void) => { delete = (req: DeleteMulticastGroupRequest, callbackFunc: () => void) => {
this.client.delete(req, SessionStore.getMetadata(), (err) => { this.client.delete(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -86,7 +85,7 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
list = (req: ListMulticastGroupsRequest, callbackFunc: (resp: ListMulticastGroupsResponse) => void) => { list = (req: ListMulticastGroupsRequest, callbackFunc: (resp: ListMulticastGroupsResponse) => void) => {
this.client.list(req, SessionStore.getMetadata(), (err, resp) => { this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
@ -97,10 +96,10 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
addDevice = (req: AddDeviceToMulticastGroupRequest, callbackFunc: () => void) => { addDevice = (req: AddDeviceToMulticastGroupRequest, callbackFunc: () => void) => {
this.client.addDevice(req, SessionStore.getMetadata(), (err) => { this.client.addDevice(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -113,10 +112,10 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
removeDevice = (req: RemoveDeviceFromMulticastGroupRequest, callbackFunc: () => void) => { removeDevice = (req: RemoveDeviceFromMulticastGroupRequest, callbackFunc: () => void) => {
this.client.removeDevice(req, SessionStore.getMetadata(), (err) => { this.client.removeDevice(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -124,7 +123,7 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
listQueue = (req: ListMulticastGroupQueueRequest, callbackFunc: (resp: ListMulticastGroupQueueResponse) => void) => { listQueue = (req: ListMulticastGroupQueueRequest, callbackFunc: (resp: ListMulticastGroupQueueResponse) => void) => {
this.client.listQueue(req, SessionStore.getMetadata(), (err, resp) => { this.client.listQueue(req, SessionStore.getMetadata(), (err, resp) => {
@ -135,7 +134,7 @@ class MulticastGroupStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
} }
const multicastGroupStore = new MulticastGroupStore(); const multicastGroupStore = new MulticastGroupStore();

View File

@ -3,7 +3,11 @@ import { Metadata } from "grpc-web";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { InternalServiceClient } from "@chirpstack/chirpstack-api-grpc-web/api/internal_grpc_web_pb"; 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 { User } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
import { HandleError, HandleLoginError } from "./helpers"; import { HandleError, HandleLoginError } from "./helpers";
@ -35,7 +39,7 @@ class SessionStore extends EventEmitter {
this.setToken(resp.getJwt()); this.setToken(resp.getJwt());
this.fetchProfile(callbackFunc); this.fetchProfile(callbackFunc);
}); });
} };
openIdConnectLogin = (req: OpenIdConnectLoginRequest, callbackFunc: any) => { openIdConnectLogin = (req: OpenIdConnectLoginRequest, callbackFunc: any) => {
this.client.openIdConnectLogin(req, {}, (err, resp) => { this.client.openIdConnectLogin(req, {}, (err, resp) => {
@ -47,7 +51,7 @@ class SessionStore extends EventEmitter {
this.setToken(resp.getToken()); this.setToken(resp.getToken());
this.fetchProfile(callbackFunc); this.fetchProfile(callbackFunc);
}); });
} };
logout = (emit: boolean, callbackFunc: () => void) => { logout = (emit: boolean, callbackFunc: () => void) => {
localStorage.clear(); localStorage.clear();
@ -59,11 +63,11 @@ class SessionStore extends EventEmitter {
} }
callbackFunc(); callbackFunc();
} };
setToken = (s: string) => { setToken = (s: string) => {
localStorage.setItem("token", s); localStorage.setItem("token", s);
} };
getToken = (): string => { getToken = (): string => {
let token = localStorage.getItem("token"); let token = localStorage.getItem("token");
@ -71,25 +75,25 @@ class SessionStore extends EventEmitter {
return ""; return "";
} }
return token; return token;
} };
getTenantId = (): string => { getTenantId = (): string => {
return localStorage.getItem("tenantId") || ""; return localStorage.getItem("tenantId") || "";
} };
setTenantId = (id: string) => { setTenantId = (id: string) => {
console.log("tenantId set to", id); console.log("tenantId set to", id);
localStorage.setItem("tenantId", id); localStorage.setItem("tenantId", id);
this.emit("tenant.change"); this.emit("tenant.change");
} };
getRowsPerPage = (): number => { getRowsPerPage = (): number => {
return parseInt(localStorage.getItem("rowsPerPage") || "10", 10); return parseInt(localStorage.getItem("rowsPerPage") || "10", 10);
} };
setRowsPerPage = (count: number) => { setRowsPerPage = (count: number) => {
localStorage.setItem("rowsPerPage", count.toString()); localStorage.setItem("rowsPerPage", count.toString());
} };
getMetadata = (): Metadata => { getMetadata = (): Metadata => {
if (this.getToken() === "") { if (this.getToken() === "") {
@ -99,7 +103,7 @@ class SessionStore extends EventEmitter {
return { return {
authorization: "Bearer " + this.getToken(), authorization: "Bearer " + this.getToken(),
}; };
} };
fetchProfile = (callbackFunc: any) => { fetchProfile = (callbackFunc: any) => {
if (this.getToken() === "") { if (this.getToken() === "") {
@ -118,11 +122,11 @@ class SessionStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
getUser = (): User | undefined => { getUser = (): User | undefined => {
return this.user; return this.user;
} };
isAdmin = (): boolean => { isAdmin = (): boolean => {
if (!this.user) { if (!this.user) {
@ -130,7 +134,7 @@ class SessionStore extends EventEmitter {
} }
return this.user.getIsAdmin(); return this.user.getIsAdmin();
} };
isTenantAdmin = (tenantId: string): boolean => { isTenantAdmin = (tenantId: string): boolean => {
for (const t of this.tenants) { for (const t of this.tenants) {
@ -140,7 +144,7 @@ class SessionStore extends EventEmitter {
} }
return false; return false;
} };
isTenantDeviceAdmin = (tenantId: string): boolean => { isTenantDeviceAdmin = (tenantId: string): boolean => {
for (const t of this.tenants) { for (const t of this.tenants) {
@ -150,7 +154,7 @@ class SessionStore extends EventEmitter {
} }
return false; return false;
} };
isTenantGatewayAdmin = (tenantId: string): boolean => { isTenantGatewayAdmin = (tenantId: string): boolean => {
for (const t of this.tenants) { for (const t of this.tenants) {
@ -158,7 +162,7 @@ class SessionStore extends EventEmitter {
} }
return false; return false;
} };
} }
const sessionStore = new SessionStore(); const sessionStore = new SessionStore();

View File

@ -22,7 +22,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class TenantStore extends EventEmitter { class TenantStore extends EventEmitter {
client: TenantServiceClient; client: TenantServiceClient;
@ -45,7 +44,7 @@ class TenantStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
get = (id: string, callbackFunc: (resp: GetTenantResponse) => void) => { get = (id: string, callbackFunc: (resp: GetTenantResponse) => void) => {
let req = new GetTenantRequest(); let req = new GetTenantRequest();
@ -59,7 +58,7 @@ class TenantStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
update = (req: UpdateTenantRequest, callbackFunc: () => void) => { update = (req: UpdateTenantRequest, callbackFunc: () => void) => {
this.client.update(req, SessionStore.getMetadata(), (err, resp) => { this.client.update(req, SessionStore.getMetadata(), (err, resp) => {
@ -77,7 +76,7 @@ class TenantStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
delete = (req: DeleteTenantRequest, callbackFunc: () => void) => { delete = (req: DeleteTenantRequest, callbackFunc: () => void) => {
this.client.delete(req, SessionStore.getMetadata(), (err, resp) => { this.client.delete(req, SessionStore.getMetadata(), (err, resp) => {
@ -93,7 +92,7 @@ class TenantStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
list = (req: ListTenantsRequest, callbackFunc: (resp: ListTenantsResponse) => void) => { list = (req: ListTenantsRequest, callbackFunc: (resp: ListTenantsResponse) => void) => {
this.client.list(req, SessionStore.getMetadata(), (err, resp) => { this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
@ -104,10 +103,10 @@ class TenantStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
addUser = (req: AddTenantUserRequest, callbackFunc: () => void) => { addUser = (req: AddTenantUserRequest, callbackFunc: () => void) => {
this.client.addUser(req, SessionStore.getMetadata(), (err) => { this.client.addUser(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -131,10 +130,10 @@ class TenantStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updateUser = (req: UpdateTenantUserRequest, callbackFunc: () => void) => { updateUser = (req: UpdateTenantUserRequest, callbackFunc: () => void) => {
this.client.updateUser(req, SessionStore.getMetadata(), (err) => { this.client.updateUser(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -147,10 +146,10 @@ class TenantStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
deleteUser = (req: DeleteTenantUserRequest, callbackFunc: () => void) => { deleteUser = (req: DeleteTenantUserRequest, callbackFunc: () => void) => {
this.client.deleteUser(req, SessionStore.getMetadata(), (err) => { this.client.deleteUser(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -163,7 +162,7 @@ class TenantStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
listUsers = (req: ListTenantUsersRequest, callbackFunc: (resp: ListTenantUsersResponse) => void) => { listUsers = (req: ListTenantUsersRequest, callbackFunc: (resp: ListTenantUsersResponse) => void) => {
this.client.listUsers(req, SessionStore.getMetadata(), (err, resp) => { this.client.listUsers(req, SessionStore.getMetadata(), (err, resp) => {
@ -174,7 +173,7 @@ class TenantStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
} }
const tenantStore = new TenantStore(); const tenantStore = new TenantStore();

View File

@ -16,7 +16,6 @@ import {
import SessionStore from "./SessionStore"; import SessionStore from "./SessionStore";
import { HandleError } from "./helpers"; import { HandleError } from "./helpers";
class UserStore extends EventEmitter { class UserStore extends EventEmitter {
client: UserServiceClient; client: UserServiceClient;
@ -39,7 +38,7 @@ class UserStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
get = (req: GetUserRequest, callbackFunc: (resp: GetUserResponse) => void) => { get = (req: GetUserRequest, callbackFunc: (resp: GetUserResponse) => void) => {
this.client.get(req, SessionStore.getMetadata(), (err, resp) => { this.client.get(req, SessionStore.getMetadata(), (err, resp) => {
@ -50,7 +49,7 @@ class UserStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
update = (req: UpdateUserRequest, callbackFunc: () => void) => { update = (req: UpdateUserRequest, callbackFunc: () => void) => {
this.client.update(req, SessionStore.getMetadata(), (err, resp) => { this.client.update(req, SessionStore.getMetadata(), (err, resp) => {
@ -66,7 +65,7 @@ class UserStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
delete = (req: DeleteUserRequest, callbackFunc: () => void) => { delete = (req: DeleteUserRequest, callbackFunc: () => void) => {
this.client.delete(req, SessionStore.getMetadata(), (err, resp) => { this.client.delete(req, SessionStore.getMetadata(), (err, resp) => {
@ -82,7 +81,7 @@ class UserStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
list = (req: ListUsersRequest, callbackFunc: (resp: ListUsersResponse) => void) => { list = (req: ListUsersRequest, callbackFunc: (resp: ListUsersResponse) => void) => {
this.client.list(req, SessionStore.getMetadata(), (err, resp) => { this.client.list(req, SessionStore.getMetadata(), (err, resp) => {
@ -93,10 +92,10 @@ class UserStore extends EventEmitter {
callbackFunc(resp); callbackFunc(resp);
}); });
} };
updatePassword = (req: UpdateUserPasswordRequest, callbackFunc: () => void) => { updatePassword = (req: UpdateUserPasswordRequest, callbackFunc: () => void) => {
this.client.updatePassword(req, SessionStore.getMetadata(), (err) => { this.client.updatePassword(req, SessionStore.getMetadata(), err => {
if (err !== null) { if (err !== null) {
HandleError(err); HandleError(err);
return; return;
@ -109,7 +108,7 @@ class UserStore extends EventEmitter {
callbackFunc(); callbackFunc();
}); });
} };
} }
const userStore = new UserStore(); const userStore = new UserStore();

View File

@ -4,7 +4,6 @@ import { Form, Input, Button } from "antd";
import { ApiKey } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb"; import { ApiKey } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
interface IProps { interface IProps {
initialValues: ApiKey; initialValues: ApiKey;
onFinish: (obj: ApiKey) => void; onFinish: (obj: ApiKey) => void;
@ -17,20 +16,18 @@ class ApiKeyForm extends Component<IProps, IState> {
let apiKey = new ApiKey(); let apiKey = new ApiKey();
apiKey.setName(values.name); apiKey.setName(values.name);
this.props.onFinish(apiKey); this.props.onFinish(apiKey);
} };
render() { render() {
return ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}> <Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
<Form.Item <Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
label="Name"
name="name"
rules={[{required: true, message: "Please enter a name!"}]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,31 +1,28 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Input, Typography, Button, Space } from 'antd'; import { Input, Typography, Button, Space } from "antd";
import { CreateApiKeyResponse } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb"; import { CreateApiKeyResponse } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
interface IProps { interface IProps {
createApiKeyResponse: CreateApiKeyResponse; createApiKeyResponse: CreateApiKeyResponse;
} }
class ApiKeyToken extends Component<IProps> { class ApiKeyToken extends Component<IProps> {
render() { render() {
return ( return (
<Space direction="vertical" style={{ width: "100%" }}> <Space direction="vertical" style={{ width: "100%" }}>
<Typography> <Typography>
<Typography.Paragraph> <Typography.Paragraph>
Use the following API token when making API requests. This token can Use the following API token when making API requests. This token can be revoked at any time by deleting it.
be revoked at any time by deleting it. Please note that this token can Please note that this token can only be retrieved once:
only be retrieved once:
</Typography.Paragraph> </Typography.Paragraph>
</Typography> </Typography>
<Input.TextArea rows={4} value={this.props.createApiKeyResponse.getToken()} /> <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> </Space>
); );
} }

View File

@ -9,14 +9,12 @@ import ApiKeyForm from "./ApiKeyForm";
import ApiKeyToken from "./ApiKeyToken"; import ApiKeyToken from "./ApiKeyToken";
import InternalStore from "../../stores/InternalStore"; import InternalStore from "../../stores/InternalStore";
interface IProps {} interface IProps {}
interface IState { interface IState {
createApiKeyResponse?: CreateApiKeyResponse; createApiKeyResponse?: CreateApiKeyResponse;
} }
class CreateAdminApiKey extends Component<IProps, IState> { class CreateAdminApiKey extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -34,7 +32,7 @@ class CreateAdminApiKey extends Component<IProps, IState> {
createApiKeyResponse: resp, createApiKeyResponse: resp,
}); });
}); });
} };
render() { render() {
const apiKey = new ApiKey(); const apiKey = new ApiKey();
@ -43,17 +41,21 @@ class CreateAdminApiKey extends Component<IProps, IState> {
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
title="Add API key" title="Add API key"
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Network-server</span> <span>Network-server</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Add</span> <span>Add</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
/> />
<Card> <Card>
{!this.state.createApiKeyResponse && <ApiKeyForm initialValues={apiKey} onFinish={this.onFinish} />} {!this.state.createApiKeyResponse && <ApiKeyForm initialValues={apiKey} onFinish={this.onFinish} />}

View File

@ -10,7 +10,6 @@ import ApiKeyForm from "./ApiKeyForm";
import ApiKeyToken from "./ApiKeyToken"; import ApiKeyToken from "./ApiKeyToken";
import InternalStore from "../../stores/InternalStore"; import InternalStore from "../../stores/InternalStore";
interface IState { interface IState {
createApiKeyResponse?: CreateApiKeyResponse; createApiKeyResponse?: CreateApiKeyResponse;
} }
@ -19,7 +18,6 @@ interface IProps {
tenant: Tenant; tenant: Tenant;
} }
class CreateTenantApiKey extends Component<IProps, IState> { class CreateTenantApiKey extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -37,7 +35,7 @@ class CreateTenantApiKey extends Component<IProps, IState> {
createApiKeyResponse: resp, createApiKeyResponse: resp,
}); });
}); });
} };
render() { render() {
const apiKey = new ApiKey(); const apiKey = new ApiKey();
@ -45,20 +43,26 @@ class CreateTenantApiKey extends Component<IProps, IState> {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Add</span> <span>Add</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="Add API key" title="Add API key"
/> />
<Card> <Card>

View File

@ -6,20 +6,23 @@ import { DeleteOutlined } from "@ant-design/icons";
import { Space, Breadcrumb, Button, PageHeader } from "antd"; import { Space, Breadcrumb, Button, PageHeader } from "antd";
import { ColumnsType } from "antd/es/table"; 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 DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
import InternalStore from "../../stores/InternalStore"; import InternalStore from "../../stores/InternalStore";
import DeleteConfirm from "../../components/DeleteConfirm"; import DeleteConfirm from "../../components/DeleteConfirm";
interface IProps {} interface IProps {}
interface IState { interface IState {
refreshKey: number; refreshKey: number;
} }
class ListAdminApiKeys extends Component<IProps, IState> { class ListAdminApiKeys extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -47,19 +50,15 @@ class ListAdminApiKeys extends Component<IProps, IState> {
key: "action", key: "action",
width: 100, width: 100,
render: (text, record) => ( render: (text, record) => (
<DeleteConfirm <DeleteConfirm typ="API key" confirm={record.name} onConfirm={this.deleteApiKey(record.id)}>
typ="API key"
confirm={record.name}
onConfirm={this.deleteApiKey(record.id)}
>
<Button shape="circle" icon={<DeleteOutlined />} /> <Button shape="circle" icon={<DeleteOutlined />} />
</DeleteConfirm> </DeleteConfirm>
), ),
}, },
]; ];
} };
deleteApiKey = (id: string): () => void => { deleteApiKey = (id: string): (() => void) => {
return () => { return () => {
let req = new DeleteApiKeyRequest(); let req = new DeleteApiKeyRequest();
req.setId(id); req.setId(id);
@ -70,8 +69,8 @@ class ListAdminApiKeys extends Component<IProps, IState> {
refreshKey: this.state.refreshKey + 1, refreshKey: this.state.refreshKey + 1,
}); });
}); });
} };
} };
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => { getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
let req = new ListApiKeysRequest(); let req = new ListApiKeysRequest();
@ -83,31 +82,30 @@ class ListAdminApiKeys extends Component<IProps, IState> {
const obj = resp.toObject(); const obj = resp.toObject();
callbackFunc(obj.totalCount, obj.resultList); callbackFunc(obj.totalCount, obj.resultList);
}); });
} };
render() { render() {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Network-server</span> <span>Network-server</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>API keys</span> <span>API keys</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="API keys" title="API keys"
extra={[ 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 <DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" refreshKey={this.state.refreshKey} />
columns={this.columns()}
getPage={this.getPage}
rowKey="id"
refreshKey={this.state.refreshKey}
/>
</Space> </Space>
); );
} }

View File

@ -5,7 +5,12 @@ import { DeleteOutlined } from "@ant-design/icons";
import { Space, Breadcrumb, Button, PageHeader } from "antd"; import { Space, Breadcrumb, Button, PageHeader } from "antd";
import { ColumnsType } from "antd/es/table"; 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 { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
@ -13,7 +18,6 @@ import InternalStore from "../../stores/InternalStore";
import DeleteConfirm from "../../components/DeleteConfirm"; import DeleteConfirm from "../../components/DeleteConfirm";
import Admin from "../../components/Admin"; import Admin from "../../components/Admin";
interface IProps { interface IProps {
tenant: Tenant; tenant: Tenant;
isAdmin: boolean; isAdmin: boolean;
@ -23,13 +27,12 @@ interface IState {
refreshKey: number; refreshKey: number;
} }
class ListTenantApiKeys extends Component<IProps, IState> { class ListTenantApiKeys extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
this.state = { this.state = {
refreshKey: 1, refreshKey: 1,
} };
} }
columns = (): ColumnsType<ApiKey.AsObject> => { columns = (): ColumnsType<ApiKey.AsObject> => {
@ -52,20 +55,16 @@ class ListTenantApiKeys extends Component<IProps, IState> {
width: 100, width: 100,
render: (text, record) => ( render: (text, record) => (
<Admin tenantId={this.props.tenant.getId()} isTenantAdmin> <Admin tenantId={this.props.tenant.getId()} isTenantAdmin>
<DeleteConfirm <DeleteConfirm typ="API key" confirm={record.name} onConfirm={this.deleteApiKey(record.id)}>
typ="API key"
confirm={record.name}
onConfirm={this.deleteApiKey(record.id)}
>
<Button shape="circle" icon={<DeleteOutlined />} /> <Button shape="circle" icon={<DeleteOutlined />} />
</DeleteConfirm> </DeleteConfirm>
</Admin> </Admin>
), ),
}, },
]; ];
} };
deleteApiKey = (id: string): () => void => { deleteApiKey = (id: string): (() => void) => {
return () => { return () => {
let req = new DeleteApiKeyRequest(); let req = new DeleteApiKeyRequest();
req.setId(id); req.setId(id);
@ -76,8 +75,8 @@ class ListTenantApiKeys extends Component<IProps, IState> {
refreshKey: this.state.refreshKey + 1, refreshKey: this.state.refreshKey + 1,
}); });
}); });
} };
} };
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => { getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
let req = new ListApiKeysRequest(); let req = new ListApiKeysRequest();
@ -89,36 +88,37 @@ class ListTenantApiKeys extends Component<IProps, IState> {
const obj = resp.toObject(); const obj = resp.toObject();
callbackFunc(obj.totalCount, obj.resultList); callbackFunc(obj.totalCount, obj.resultList);
}); });
} };
render() { render() {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>API Keys</span> <span>API Keys</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="API keys" title="API keys"
extra={[ extra={[
<Admin tenantId={this.props.tenant.getId()} isTenantAdmin> <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> <Button type="primary">
</Admin> <Link to={`/tenants/${this.props.tenant.getId()}/api-keys/create`}>Add API key</Link>
</Button>
</Admin>,
]} ]}
/> />
<DataTable <DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" refreshKey={this.state.refreshKey} />
columns={this.columns()}
getPage={this.getPage}
rowKey="id"
refreshKey={this.state.refreshKey}
/>
</Space> </Space>
); );
} }

View File

@ -3,7 +3,6 @@ import React, { Component } from "react";
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb"; import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import { Form, Input, Button } from "antd"; import { Form, Input, Button } from "antd";
interface IProps { interface IProps {
initialValues: Application; initialValues: Application;
onFinish: (obj: Application) => void; onFinish: (obj: Application) => void;
@ -21,26 +20,21 @@ class ApplicationForm extends Component<IProps> {
app.setDescription(v.description); app.setDescription(v.description);
this.props.onFinish(app); this.props.onFinish(app);
} };
render() { render() {
return ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}> <Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish}>
<Form.Item <Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
label="Name"
name="name"
rules={[{required: true, message: "Please enter a name!"}]}
>
<Input disabled={this.props.disabled} /> <Input disabled={this.props.disabled} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label="Description" name="description">
label="Description"
name="description"
>
<Input.TextArea disabled={this.props.disabled} /> <Input.TextArea disabled={this.props.disabled} />
</Form.Item> </Form.Item>
<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.Item>
</Form> </Form>
); );

View File

@ -35,13 +35,11 @@ import CreateThingsBoardIntegration from "./integrations/CreateThingsBoardIntegr
import EditThingsBoardIntegration from "./integrations/EditThingsBoardIntegration"; import EditThingsBoardIntegration from "./integrations/EditThingsBoardIntegration";
import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate"; import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
tenant: Tenant; tenant: Tenant;
application: Application; application: Application;
} }
class ApplicationLayout extends Component<IProps> { class ApplicationLayout extends Component<IProps> {
deleteApplication = () => { deleteApplication = () => {
let req = new DeleteApplicationRequest(); let req = new DeleteApplicationRequest();
@ -50,7 +48,7 @@ class ApplicationLayout extends Component<IProps> {
ApplicationStore.delete(req, () => { ApplicationStore.delete(req, () => {
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications`); this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications`);
}); });
} };
render() { render() {
const tenant = this.props.tenant; const tenant = this.props.tenant;
@ -73,71 +71,178 @@ class ApplicationLayout extends Component<IProps> {
tab = "integrations"; 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 ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>{app.getName()}</span> <span>{app.getName()}</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title={app.getName()} title={app.getName()}
subTitle={`application id: ${app.getId()}`} subTitle={`application id: ${app.getId()}`}
extra={[ extra={[
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin> <Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
<DeleteConfirm <DeleteConfirm confirm={app.getName()} typ="application" onConfirm={this.deleteApplication}>
confirm={app.getName()} <Button danger type="primary">
typ="application" Delete application
onConfirm={this.deleteApplication} </Button>
>
<Button danger type="primary">Delete application</Button>
</DeleteConfirm> </DeleteConfirm>
</Admin> </Admin>,
]} ]}
/> />
<Card> <Card>
<Menu mode="horizontal" selectedKeys={[tab]} style={{ marginBottom: 24 }}> <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="devices">
<Menu.Item key="mg"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/multicast-groups`}>Multicast groups</Link></Menu.Item> <Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}`}>Devices</Link>
<Menu.Item key="edit"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/edit`}>Application configuration</Link></Menu.Item> </Menu.Item>
{showIntegrations && <Menu.Item key="integrations"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/integrations`}>Integrations</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> </Menu>
<Switch> <Switch>
<Route exact path={this.props.match.path} render={props => <ListDevices application={app} {...props} />} /> <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
<Route exact path={`${this.props.match.path}/integrations`} render={props => <ListIntegrations application={app} {...props} />} /> exact
<Route exact path={`${this.props.match.path}/multicast-groups`} render={props => <ListMulticastGroups application={app} {...props} />} /> 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
<Route exact path={`${this.props.match.path}/integrations/http/edit`} render={props => <EditHttpIntegration application={app} {...props} />} /> exact
<Route exact path={`${this.props.match.path}/integrations/aws-sns/create`} render={props => <CreateAwsSnsIntegration application={app} {...props} />} /> path={`${this.props.match.path}/integrations/http/create`}
<Route exact path={`${this.props.match.path}/integrations/aws-sns/edit`} render={props => <EditAwsSnsIntegration application={app} {...props} />} /> render={props => <CreateHttpIntegration 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
<Route exact path={`${this.props.match.path}/integrations/gcp-pub-sub/create`} render={props => <CreateGcpPubSubIntegration application={app} {...props} />} /> exact
<Route exact path={`${this.props.match.path}/integrations/gcp-pub-sub/edit`} render={props => <EditGcpPubSubIntegration application={app} {...props} />} /> path={`${this.props.match.path}/integrations/http/edit`}
<Route exact path={`${this.props.match.path}/integrations/influxdb/create`} render={props => <CreateInfluxDbIntegration application={app} {...props} />} /> render={props => <EditHttpIntegration 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
<Route exact path={`${this.props.match.path}/integrations/mydevices/edit`} render={props => <EditMyDevicesIntegration application={app} {...props} />} /> exact
<Route exact path={`${this.props.match.path}/integrations/pilot-things/create`} render={props => <CreatePilotThingsIntegration application={app} {...props} />} /> path={`${this.props.match.path}/integrations/aws-sns/create`}
<Route exact path={`${this.props.match.path}/integrations/pilot-things/edit`} render={props => <EditPilotThingsIntegration application={app} {...props} />} /> render={props => <CreateAwsSnsIntegration 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
<Route exact path={`${this.props.match.path}/integrations/thingsboard/create`} render={props => <CreateThingsBoardIntegration application={app} {...props} />} /> exact
<Route exact path={`${this.props.match.path}/integrations/thingsboard/edit`} render={props => <EditThingsBoardIntegration application={app} {...props} />} /> path={`${this.props.match.path}/integrations/aws-sns/edit`}
<Route exact path={`${this.props.match.path}/integrations/mqtt/certificate`} render={props => <GenerateMqttCertificate application={app} {...props} />} /> 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> </Switch>
</Card> </Card>
</Space> </Space>

View File

@ -2,7 +2,11 @@ import React, { Component } from "react";
import { Route, Switch, RouteComponentProps } from "react-router-dom"; import { Route, Switch, RouteComponentProps } from "react-router-dom";
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import { Application, 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 ApplicationStore from "../../stores/ApplicationStore";
import ApplicationLayout from "./ApplicationLayout"; import ApplicationLayout from "./ApplicationLayout";
@ -11,7 +15,6 @@ import DeviceLayout from "../devices/DeviceLayout";
import MulticastGroupLayout from "../multicast-groups/MulticastGroupLayout"; import MulticastGroupLayout from "../multicast-groups/MulticastGroupLayout";
import CreateMulticastGroup from "../multicast-groups/CreateMulticastGroup"; import CreateMulticastGroup from "../multicast-groups/CreateMulticastGroup";
interface MatchParams { interface MatchParams {
applicationId: string; applicationId: string;
} }
@ -24,7 +27,6 @@ interface IState {
application?: Application; application?: Application;
} }
class ApplicationLoader extends Component<IProps, IState> { class ApplicationLoader extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -44,7 +46,7 @@ class ApplicationLoader extends Component<IProps, IState> {
application: resp.getApplication(), application: resp.getApplication(),
}); });
}); });
} };
render() { render() {
const app = this.state.application; const app = this.state.application;
@ -55,13 +57,26 @@ class ApplicationLoader extends Component<IProps, IState> {
const path = this.props.match.path; const path = this.props.match.path;
const tenant = this.props.tenant; const tenant = this.props.tenant;
return ( return (
<Switch> <Switch>
<Route exact path={`${path}/devices/create`} render={props => <CreateDevice tenant={tenant} application={app} {...props} />} /> <Route
<Route exact path={`${path}/multicast-groups/create`} render={props => <CreateMulticastGroup tenant={tenant} application={app} {...props} />} /> exact
<Route path={`${path}/multicast-groups/:multicastGroupId([\\w-]{36})`} render={(props: any) => <MulticastGroupLayout tenant={tenant} application={app} {...props} />} /> path={`${path}/devices/create`}
<Route path={`${path}/devices/:devEui([0-9a-f]{16})`} component={(props: any) => <DeviceLayout tenant={tenant} application={app} {...props} />} /> 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} />} /> <Route path={path} render={props => <ApplicationLayout tenant={tenant} application={app} {...props} />} />
</Switch> </Switch>
); );

View File

@ -4,17 +4,19 @@ import { Link, RouteComponentProps } from "react-router-dom";
import { Space, Breadcrumb, Card, PageHeader } from "antd"; import { Space, Breadcrumb, Card, PageHeader } from "antd";
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; 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 ApplicationForm from "./ApplicationForm";
import ApplicationStore from "../../stores/ApplicationStore"; import ApplicationStore from "../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
tenant: Tenant; tenant: Tenant;
} }
class CreateApplication extends Component<IProps> { class CreateApplication extends Component<IProps> {
onFinish = (obj: Application) => { onFinish = (obj: Application) => {
obj.setTenantId(this.props.tenant.getId()); obj.setTenantId(this.props.tenant.getId());
@ -25,7 +27,7 @@ class CreateApplication extends Component<IProps> {
ApplicationStore.create(req, (resp: CreateApplicationResponse) => { ApplicationStore.create(req, (resp: CreateApplicationResponse) => {
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${resp.getId()}`); this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${resp.getId()}`);
}); });
} };
render() { render() {
const app = new Application(); const app = new Application();
@ -33,20 +35,26 @@ class CreateApplication extends Component<IProps> {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Add</span> <span>Add</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="Add application" title="Add application"
/> />
<Card> <Card>

View File

@ -7,28 +7,30 @@ import ApplicationStore from "../../stores/ApplicationStore";
import ApplicationForm from "./ApplicationForm"; import ApplicationForm from "./ApplicationForm";
import SessionStore from "../../stores/SessionStore"; import SessionStore from "../../stores/SessionStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application, application: Application;
} }
class EditApplication extends Component<IProps> { class EditApplication extends Component<IProps> {
onFinish = (obj: Application) => { onFinish = (obj: Application) => {
let req = new UpdateApplicationRequest(); let req = new UpdateApplicationRequest();
req.setApplication(obj); req.setApplication(obj);
ApplicationStore.update(req, () => { ApplicationStore.update(req, () => {
this.props.history.push(`/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() { render() {
const disabled = !(SessionStore.isAdmin() || SessionStore.isTenantAdmin(this.props.application.getTenantId()) || SessionStore.isTenantDeviceAdmin(this.props.application.getTenantId())); const disabled = !(
SessionStore.isAdmin() ||
return( SessionStore.isTenantAdmin(this.props.application.getTenantId()) ||
<ApplicationForm initialValues={this.props.application} disabled={disabled} onFinish={this.onFinish} /> SessionStore.isTenantDeviceAdmin(this.props.application.getTenantId())
); );
return <ApplicationForm initialValues={this.props.application} disabled={disabled} onFinish={this.onFinish} />;
} }
} }

View File

@ -4,19 +4,21 @@ import { Link } from "react-router-dom";
import { Space, Breadcrumb, Button, PageHeader } from "antd"; import { Space, Breadcrumb, Button, PageHeader } from "antd";
import { ColumnsType } from "antd/es/table"; 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 { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import DataTable, { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
import ApplicationStore from "../../stores/ApplicationStore"; import ApplicationStore from "../../stores/ApplicationStore";
import Admin from "../../components/Admin"; import Admin from "../../components/Admin";
interface IProps { interface IProps {
tenant: Tenant; tenant: Tenant;
} }
class ListApplications extends Component<IProps> { class ListApplications extends Component<IProps> {
columns = (): ColumnsType<ApplicationListItem.AsObject> => { columns = (): ColumnsType<ApplicationListItem.AsObject> => {
return [ return [
@ -35,7 +37,7 @@ class ListApplications extends Component<IProps> {
key: "description", key: "description",
}, },
]; ];
} };
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => { getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
let req = new ListApplicationsRequest(); let req = new ListApplicationsRequest();
@ -47,35 +49,37 @@ class ListApplications extends Component<IProps> {
const obj = resp.toObject(); const obj = resp.toObject();
callbackFunc(obj.totalCount, obj.resultList); callbackFunc(obj.totalCount, obj.resultList);
}); });
} };
render() { render() {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Applications</span> <span>Applications</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="Applications" title="Applications"
extra={[ extra={[
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin> <Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
<Button type="primary"><Link to={`/tenants/${this.props.tenant.getId()}/applications/create`}>Add application</Link></Button> <Button type="primary">
</Admin> <Link to={`/tenants/${this.props.tenant.getId()}/applications/create`}>Add application</Link>
</Button>
</Admin>,
]} ]}
/> />
<DataTable <DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" />
columns={this.columns()}
getPage={this.getPage}
rowKey="id"
/>
</Space> </Space>
); );
} }

View File

@ -22,7 +22,6 @@ import PilotThingsCard from "./integrations/PilotThingsCard";
import LoRaCloudCard from "./integrations/LoRaCloudCard"; import LoRaCloudCard from "./integrations/LoRaCloudCard";
import ThingsBoardCard from "./integrations/ThingsBoardCard"; import ThingsBoardCard from "./integrations/ThingsBoardCard";
interface IProps { interface IProps {
application: Application; application: Application;
} }
@ -32,7 +31,6 @@ interface IState {
available: any[]; available: any[];
} }
class ListIntegrations extends Component<IProps, IState> { class ListIntegrations extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -137,13 +135,12 @@ class ListIntegrations extends Component<IProps, IState> {
available.push(<ThingsBoardCard application={this.props.application} add />); available.push(<ThingsBoardCard application={this.props.application} add />);
} }
this.setState({ this.setState({
configured: configured, configured: configured,
available: available, available: available,
}); });
}); });
} };
render() { render() {
return ( return (

View File

@ -1,45 +1,41 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import { Application, DeleteAwsSnsIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
Application,
DeleteAwsSnsIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class AwsSns extends Component<IProps> { class AwsSns extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteAwsSnsIntegrationRequest(); let req = new DeleteAwsSnsIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteAwsSnsIntegration(req, () => {}); ApplicationStore.deleteAwsSnsIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/aws-sns/create"><PlusOutlined /></Link> <Link to="integrations/aws-sns/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/aws-sns/edit"><EditOutlined /></Link>, <Link to="integrations/aws-sns/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -53,9 +49,7 @@ class AwsSns extends Component<IProps> {
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} actions={actions}
> >
<Card.Meta <Card.Meta description="The AWS SNS integration forwards events to an AWS SNS topic." />
description="The AWS SNS integration forwards events to an AWS SNS topic."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -2,11 +2,7 @@ import React, { Component } from "react";
import { Form, Input, Button, Select } from "antd"; import { Form, Input, Button, Select } from "antd";
import { import { AwsSnsIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
AwsSnsIntegration,
Encoding,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: AwsSnsIntegration; initialValues: AwsSnsIntegration;
@ -26,7 +22,7 @@ class AwsSnsIntegrationForm extends Component<IProps> {
i.setTopicArn(v.topicArn); i.setTopicArn(v.topicArn);
this.props.onFinish(i); this.props.onFinish(i);
} };
render() { render() {
return ( return (
@ -41,11 +37,7 @@ class AwsSnsIntegrationForm extends Component<IProps> {
<Select.Option value={Encoding.PROTOBUF}>Protobuf (binary)</Select.Option> <Select.Option value={Encoding.PROTOBUF}>Protobuf (binary)</Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item <Form.Item label="AWS region" name="region" rules={[{ required: true, message: "Please enter a region!" }]}>
label="AWS region"
name="region"
rules={[{required: true, message: "Please enter a region!"}]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@ -70,7 +62,9 @@ class AwsSnsIntegrationForm extends Component<IProps> {
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,7 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import {
@ -11,34 +11,33 @@ import {
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class AzureServiceBusCard extends Component<IProps> { class AzureServiceBusCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteAzureServiceBusIntegrationRequest(); let req = new DeleteAzureServiceBusIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteAzureServiceBusIntegration(req, () => {}); ApplicationStore.deleteAzureServiceBusIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/azure-service-bus/create"><PlusOutlined /></Link> <Link to="integrations/azure-service-bus/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/azure-service-bus/edit"><EditOutlined /></Link>, <Link to="integrations/azure-service-bus/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +51,7 @@ class AzureServiceBusCard extends Component<IProps> {
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} actions={actions}
> >
<Card.Meta <Card.Meta description="The Azure Service-Bus integration forwards events to an Azure Service-Bus topic or queue." />
description="The Azure Service-Bus integration forwards events to an Azure Service-Bus topic or queue."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -2,18 +2,13 @@ import React, { Component } from "react";
import { Form, Input, Button, Select } from "antd"; import { Form, Input, Button, Select } from "antd";
import { import { AzureServiceBusIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
AzureServiceBusIntegration,
Encoding,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: AzureServiceBusIntegration; initialValues: AzureServiceBusIntegration;
onFinish: (obj: AzureServiceBusIntegration) => void; onFinish: (obj: AzureServiceBusIntegration) => void;
} }
class AzureServiceBusIntegrationForm extends Component<IProps> { class AzureServiceBusIntegrationForm extends Component<IProps> {
onFinish = (values: AzureServiceBusIntegration.AsObject) => { onFinish = (values: AzureServiceBusIntegration.AsObject) => {
const v = Object.assign(this.props.initialValues.toObject(), values); const v = Object.assign(this.props.initialValues.toObject(), values);
@ -25,7 +20,7 @@ class AzureServiceBusIntegrationForm extends Component<IProps> {
i.setPublishName(v.publishName); i.setPublishName(v.publishName);
this.props.onFinish(i); this.props.onFinish(i);
} };
render() { render() {
return ( return (
@ -56,7 +51,9 @@ class AzureServiceBusIntegrationForm extends Component<IProps> {
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -24,9 +24,11 @@ class CreateAwsSnsIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createAwsSnsIntegration(req, () => { 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() { render() {
const i = new AwsSnsIntegration(); const i = new AwsSnsIntegration();

View File

@ -12,12 +12,10 @@ import {
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm"; import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
class CreateAzureServiceBusIntegration extends Component<IProps> { class CreateAzureServiceBusIntegration extends Component<IProps> {
onFinish = (obj: AzureServiceBusIntegration) => { onFinish = (obj: AzureServiceBusIntegration) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -26,9 +24,11 @@ class CreateAzureServiceBusIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createAzureServiceBusIntegration(req, () => { 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() { render() {
const i = new AzureServiceBusIntegration(); const i = new AzureServiceBusIntegration();

View File

@ -12,12 +12,10 @@ import {
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm"; import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
class CreateGcpPubSubIntegration extends Component<IProps> { class CreateGcpPubSubIntegration extends Component<IProps> {
onFinish = (obj: GcpPubSubIntegration) => { onFinish = (obj: GcpPubSubIntegration) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -26,9 +24,11 @@ class CreateGcpPubSubIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createGcpPubSubIntegration(req, () => { 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() { render() {
const i = new GcpPubSubIntegration(); const i = new GcpPubSubIntegration();

View File

@ -12,7 +12,6 @@ import {
import HttpIntegrationForm from "./HttpIntegrationForm"; import HttpIntegrationForm from "./HttpIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
@ -25,9 +24,11 @@ class CreateHttpIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createHttpIntegration(req, () => { 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() { render() {
const i = new HttpIntegration(); const i = new HttpIntegration();

View File

@ -12,12 +12,10 @@ import {
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm"; import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
class CreateInfluxDbIntegration extends Component<IProps> { class CreateInfluxDbIntegration extends Component<IProps> {
onFinish = (obj: InfluxDbIntegration) => { onFinish = (obj: InfluxDbIntegration) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -26,9 +24,11 @@ class CreateInfluxDbIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createInfluxDbIntegration(req, () => { 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() { render() {
const i = new InfluxDbIntegration(); const i = new InfluxDbIntegration();

View File

@ -10,16 +10,13 @@ import {
CreateLoraCloudIntegrationRequest, CreateLoraCloudIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb"; } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm"; import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
class CreateLoRaCloudIntegration extends Component<IProps> { class CreateLoRaCloudIntegration extends Component<IProps> {
onFinish = (obj: LoraCloudIntegration) => { onFinish = (obj: LoraCloudIntegration) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -28,9 +25,11 @@ class CreateLoRaCloudIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createLoraCloudIntegration(req, () => { 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() { render() {
let i = new LoraCloudIntegration(); let i = new LoraCloudIntegration();
@ -41,7 +40,6 @@ class CreateLoRaCloudIntegration extends Component<IProps> {
i.setModemGeolocationServices(mgs); i.setModemGeolocationServices(mgs);
return ( return (
<Card title="Add Semtech LoRa Cloud&trade; integration"> <Card title="Add Semtech LoRa Cloud&trade; integration">
<LoRaCloudIntegrationForm initialValues={i} onFinish={this.onFinish} /> <LoRaCloudIntegrationForm initialValues={i} onFinish={this.onFinish} />

View File

@ -12,12 +12,10 @@ import {
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm"; import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
class CreateMyDevicesIntegration extends Component<IProps> { class CreateMyDevicesIntegration extends Component<IProps> {
onFinish = (obj: MyDevicesIntegration) => { onFinish = (obj: MyDevicesIntegration) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -26,9 +24,11 @@ class CreateMyDevicesIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createMyDevicesIntegration(req, () => { 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() { render() {
const i = new MyDevicesIntegration(); const i = new MyDevicesIntegration();

View File

@ -12,12 +12,10 @@ import {
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm"; import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
class CreatePilotThingsIntegration extends Component<IProps> { class CreatePilotThingsIntegration extends Component<IProps> {
onFinish = (obj: PilotThingsIntegration) => { onFinish = (obj: PilotThingsIntegration) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -26,9 +24,11 @@ class CreatePilotThingsIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createPilotThingsIntegration(req, () => { 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() { render() {
const i = new PilotThingsIntegration(); const i = new PilotThingsIntegration();

View File

@ -12,12 +12,10 @@ import {
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm"; import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
class CreateThingsBoardIntegration extends Component<IProps> { class CreateThingsBoardIntegration extends Component<IProps> {
onFinish = (obj: ThingsBoardIntegration) => { onFinish = (obj: ThingsBoardIntegration) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -26,9 +24,11 @@ class CreateThingsBoardIntegration extends Component<IProps> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.createThingsBoardIntegration(req, () => { 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() { render() {
const i = new ThingsBoardIntegration(); const i = new ThingsBoardIntegration();

View File

@ -22,7 +22,6 @@ interface IState {
integration?: AwsSnsIntegration; integration?: AwsSnsIntegration;
} }
class EditAwsSnsIntegration extends Component<IProps, IState> { class EditAwsSnsIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -45,9 +44,11 @@ class EditAwsSnsIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateAwsSnsIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,17 +14,14 @@ import {
import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm"; import AzureServiceBusIntegrationForm from "./AzureServiceBusIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
interface IState { interface IState {
integration?: AzureServiceBusIntegration; integration?: AzureServiceBusIntegration;
} }
class EditAzureServiceBusIntegration extends Component<IProps, IState> { class EditAzureServiceBusIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -47,9 +44,11 @@ class EditAzureServiceBusIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateAzureServiceBusIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,17 +14,14 @@ import {
import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm"; import GcpPubSubIntegrationForm from "./GcpPubSubIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
interface IState { interface IState {
integration?: GcpPubSubIntegration; integration?: GcpPubSubIntegration;
} }
class EditGcpPubSubIntegration extends Component<IProps, IState> { class EditGcpPubSubIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -47,9 +44,11 @@ class EditGcpPubSubIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateGcpPubSubIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,7 +14,6 @@ import {
import HttpIntegrationForm from "./HttpIntegrationForm"; import HttpIntegrationForm from "./HttpIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
@ -45,9 +44,11 @@ class EditHttpIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateHttpIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,17 +14,14 @@ import {
import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm"; import InfluxDbIntegrationForm from "./InfluxDbIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
interface IState { interface IState {
integration?: InfluxDbIntegration; integration?: InfluxDbIntegration;
} }
class EditInfluxDbIntegration extends Component<IProps, IState> { class EditInfluxDbIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -47,10 +44,11 @@ class EditInfluxDbIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateInfluxDbIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,17 +14,14 @@ import {
import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm"; import LoRaCloudIntegrationForm from "./LoRaCloudIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
interface IState { interface IState {
integration?: LoraCloudIntegration; integration?: LoraCloudIntegration;
} }
class EditLoRaCloudIntegration extends Component<IProps, IState> { class EditLoRaCloudIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -47,9 +44,11 @@ class EditLoRaCloudIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateLoraCloudIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,7 +14,6 @@ import {
import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm"; import MyDevicesIntegrationForm from "./MyDevicesIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
@ -23,7 +22,6 @@ interface IState {
integration?: MyDevicesIntegration; integration?: MyDevicesIntegration;
} }
class EditMyDevicesIntegration extends Component<IProps, IState> { class EditMyDevicesIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -46,9 +44,11 @@ class EditMyDevicesIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateMyDevicesIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,17 +14,14 @@ import {
import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm"; import PilotThingsIntegrationForm from "./PilotThingsIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
interface IState { interface IState {
integration?: PilotThingsIntegration; integration?: PilotThingsIntegration;
} }
class EditPilotThingsIntegration extends Component<IProps, IState> { class EditPilotThingsIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -47,9 +44,11 @@ class EditPilotThingsIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updatePilotThingsIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -14,7 +14,6 @@ import {
import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm"; import ThingsBoardIntegrationForm from "./ThingsBoardIntegrationForm";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
application: Application; application: Application;
} }
@ -23,7 +22,6 @@ interface IState {
integration?: ThingsBoardIntegration; integration?: ThingsBoardIntegration;
} }
class EditThingsBoardIntegration extends Component<IProps, IState> { class EditThingsBoardIntegration extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -46,9 +44,11 @@ class EditThingsBoardIntegration extends Component<IProps, IState> {
req.setIntegration(obj); req.setIntegration(obj);
ApplicationStore.updateThingsBoardIntegration(req, () => { 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() { render() {
if (this.state.integration === undefined) { if (this.state.integration === undefined) {

View File

@ -1,44 +1,40 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import { Application, DeleteGcpPubSubIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
Application,
DeleteGcpPubSubIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class GcpPubSubCard extends Component<IProps> { class GcpPubSubCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteGcpPubSubIntegrationRequest(); let req = new DeleteGcpPubSubIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteGcpPubSubIntegration(req, () => {}); ApplicationStore.deleteGcpPubSubIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/gcp-pub-sub/create"><PlusOutlined /></Link> <Link to="integrations/gcp-pub-sub/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/gcp-pub-sub/edit"><EditOutlined /></Link>, <Link to="integrations/gcp-pub-sub/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +48,7 @@ class GcpPubSubCard extends Component<IProps> {
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} actions={actions}
> >
<Card.Meta <Card.Meta description="The Google Cloud Pub/Sub integration forwards events to a GCP Pub/Sub topic." />
description="The Google Cloud Pub/Sub integration forwards events to a GCP Pub/Sub topic."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -2,18 +2,13 @@ import React, { Component } from "react";
import { Form, Input, Button, Select } from "antd"; import { Form, Input, Button, Select } from "antd";
import { import { GcpPubSubIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
GcpPubSubIntegration,
Encoding,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: GcpPubSubIntegration; initialValues: GcpPubSubIntegration;
onFinish: (obj: GcpPubSubIntegration) => void; onFinish: (obj: GcpPubSubIntegration) => void;
} }
class GcpPubSubIntegrationForm extends Component<IProps> { class GcpPubSubIntegrationForm extends Component<IProps> {
onFinish = (values: GcpPubSubIntegration.AsObject) => { onFinish = (values: GcpPubSubIntegration.AsObject) => {
const v = Object.assign(this.props.initialValues.toObject(), values); const v = Object.assign(this.props.initialValues.toObject(), values);
@ -26,7 +21,7 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
i.setCredentialsFile(v.credentialsFile); i.setCredentialsFile(v.credentialsFile);
this.props.onFinish(i); this.props.onFinish(i);
} };
render() { render() {
return ( return (
@ -64,7 +59,9 @@ class GcpPubSubIntegrationForm extends Component<IProps> {
<Input.TextArea rows={10} /> <Input.TextArea rows={10} />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,7 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import moment from "moment"; import moment from "moment";
import { Card, Button, Form, Input } from 'antd'; import { Card, Button, Form, Input } from "antd";
import { import {
Application, Application,
@ -11,18 +11,15 @@ import {
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application; application: Application;
} }
interface IState { interface IState {
certificate?: GenerateMqttIntegrationClientCertificateResponse; certificate?: GenerateMqttIntegrationClientCertificateResponse;
buttonDisabled: boolean; buttonDisabled: boolean;
} }
class GenerateMqttCertificate extends Component<IProps, IState> { class GenerateMqttCertificate extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -40,28 +37,35 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
let req = new GenerateMqttIntegrationClientCertificateRequest(); let req = new GenerateMqttIntegrationClientCertificateRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.generateMqttIntegrationClientCertificate(req, (resp: GenerateMqttIntegrationClientCertificateResponse) => { ApplicationStore.generateMqttIntegrationClientCertificate(
req,
(resp: GenerateMqttIntegrationClientCertificateResponse) => {
this.setState({ this.setState({
certificate: resp, certificate: resp,
}); });
}); },
} );
};
renderRequest = () => { renderRequest = () => {
return ( return (
<Card> <Card>
<p> <p>
If required by the network, the MQTT client needs to be configured with a client certificate If required by the network, the MQTT client needs to be configured with a client certificate in order to
in order to connect to the MQTT broker to receive device data. The generated certificate is connect to the MQTT broker to receive device data. The generated certificate is application specific.
application specific. <strong>
<strong> Please note the expiration of the certificate and make sure to renew the certificate on time!</strong> {" "}
Please note the expiration of the certificate and make sure to renew the certificate on time!
</strong>
</p> </p>
<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> </p>
</Card> </Card>
); );
} };
renderResponse = () => { renderResponse = () => {
const certificate = this.state.certificate!; const certificate = this.state.certificate!;
@ -89,23 +93,15 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
> >
<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>
<Form.Item <Form.Item label="TLS certificate" name="tlsCert" tooltip="Store this as a text-file, e.g. named 'cert.crt'">
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" }} /> <Input.TextArea autoSize disabled style={{ cursor: "text", fontFamily: "monospace", color: "black" }} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label="TLS key" name="tlsKey" tooltip="Store this as a text-file, e.g. named 'cert.key'">
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" }} /> <Input.TextArea autoSize disabled style={{ cursor: "text", fontFamily: "monospace", color: "black" }} />
</Form.Item> </Form.Item>
</Form> </Form>
); );
} };
render() { render() {
let content = this.renderRequest(); let content = this.renderRequest();
@ -114,11 +110,7 @@ class GenerateMqttCertificate extends Component<IProps, IState> {
content = this.renderResponse(); content = this.renderResponse();
} }
return( return <Card title="Generate MQTT certificate">{content}</Card>;
<Card title="Generate MQTT certificate">
{content}
</Card>
);
} }
} }

View File

@ -1,44 +1,40 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import { Application, DeleteHttpIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
Application,
DeleteHttpIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class HttpCard extends Component<IProps> { class HttpCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteHttpIntegrationRequest(); let req = new DeleteHttpIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteHttpIntegration(req, () => {}); ApplicationStore.deleteHttpIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/http/create"><PlusOutlined /></Link> <Link to="integrations/http/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/http/edit"><EditOutlined /></Link>, <Link to="integrations/http/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +48,7 @@ class HttpCard extends Component<IProps> {
cover={<img alt="HTTP" src="/integrations/http.png" style={{ padding: 1 }} />} cover={<img alt="HTTP" src="/integrations/http.png" style={{ padding: 1 }} />}
actions={actions} actions={actions}
> >
<Card.Meta <Card.Meta description="The HTTP integration forwards events to a user-configurable endpoint as POST requests." />
description="The HTTP integration forwards events to a user-configurable endpoint as POST requests."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -1,13 +1,9 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Form, Input, Button, Select, Row, Col, Typography, Space } from "antd"; import { Form, Input, Button, Select, Row, Col, Typography, Space } from "antd";
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
import {
HttpIntegration,
Encoding,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import { HttpIntegration, Encoding } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: HttpIntegration; initialValues: HttpIntegration;
@ -29,7 +25,7 @@ class HttpIntegrationForm extends Component<IProps> {
} }
this.props.onFinish(i); this.props.onFinish(i);
} };
render() { render() {
return ( return (
@ -64,7 +60,7 @@ class HttpIntegrationForm extends Component<IProps> {
{...restField} {...restField}
name={[name, 0]} name={[name, 0]}
fieldKey={[name, 0]} fieldKey={[name, 0]}
rules={[{ required: true, message: 'Please enter a key!' }]} rules={[{ required: true, message: "Please enter a key!" }]}
> >
<Input placeholder="Key" /> <Input placeholder="Key" />
</Form.Item> </Form.Item>
@ -74,7 +70,7 @@ class HttpIntegrationForm extends Component<IProps> {
{...restField} {...restField}
name={[name, 1]} name={[name, 1]}
fieldKey={[name, 1]} fieldKey={[name, 1]}
rules={[{ required: true, message: 'Please enter a value!' }]} rules={[{ required: true, message: "Please enter a value!" }]}
> >
<Input placeholder="Value" /> <Input placeholder="Value" />
</Form.Item> </Form.Item>
@ -94,7 +90,9 @@ class HttpIntegrationForm extends Component<IProps> {
</Form.List> </Form.List>
</Space> </Space>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -8,18 +8,15 @@ import {
InfluxDbVersion, InfluxDbVersion,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb"; } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: InfluxDbIntegration; initialValues: InfluxDbIntegration;
onFinish: (obj: InfluxDbIntegration) => void; onFinish: (obj: InfluxDbIntegration) => void;
} }
interface IState { interface IState {
selectedVersion: InfluxDbVersion; selectedVersion: InfluxDbVersion;
} }
class InfluxDbIntegrationForm extends Component<IProps, IState> { class InfluxDbIntegrationForm extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -45,13 +42,13 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
i.setToken(v.token); i.setToken(v.token);
this.props.onFinish(i); this.props.onFinish(i);
} };
onVersionChange = (version: InfluxDbVersion) => { onVersionChange = (version: InfluxDbVersion) => {
this.setState({ this.setState({
selectedVersion: version, selectedVersion: version,
}); });
} };
render() { render() {
return ( return (
@ -71,40 +68,38 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
name="endpoint" name="endpoint"
rules={[{ required: true, message: "Please enter an endpoint!" }]} rules={[{ required: true, message: "Please enter an endpoint!" }]}
> >
<Input <Input placeholder="http://localhost:8086/api/v2/write" />
placeholder="http://localhost:8086/api/v2/write"
/>
</Form.Item> </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item {this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
label="Username" <Form.Item label="Username" name="username">
name="username"
>
<Input /> <Input />
</Form.Item>} </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item )}
label="Password" {this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
name="password" <Form.Item label="Password" name="password">
>
<Input.Password /> <Input.Password />
</Form.Item>} </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item )}
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
<Form.Item
label="Database name" label="Database name"
name="db" name="db"
rules={[{ required: true, message: "Please enter database name!" }]} rules={[{ required: true, message: "Please enter database name!" }]}
> >
<Input /> <Input />
</Form.Item>} </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item )}
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
<Form.Item
label="Retention policy name" label="Retention policy name"
name="retentionPolicyName" 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." 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 /> <Input />
</Form.Item>} </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && <Form.Item )}
label="Select timestamp precision" {this.state.selectedVersion === InfluxDbVersion.INFLUXDB_1 && (
name="precision" <Form.Item label="Select timestamp precision" name="precision">
>
<Select> <Select>
<Select.Option value={InfluxDbPrecision.NS}>Nanosecond</Select.Option> <Select.Option value={InfluxDbPrecision.NS}>Nanosecond</Select.Option>
<Select.Option value={InfluxDbPrecision.U}>Microsecond</Select.Option> <Select.Option value={InfluxDbPrecision.U}>Microsecond</Select.Option>
@ -113,27 +108,27 @@ class InfluxDbIntegrationForm extends Component<IProps, IState> {
<Select.Option value={InfluxDbPrecision.M}>Minute</Select.Option> <Select.Option value={InfluxDbPrecision.M}>Minute</Select.Option>
<Select.Option value={InfluxDbPrecision.H}>Hour</Select.Option> <Select.Option value={InfluxDbPrecision.H}>Hour</Select.Option>
</Select> </Select>
</Form.Item>} </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && <Form.Item )}
label="Organization" {this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
name="organization" <Form.Item label="Organization" name="organization">
>
<Input /> <Input />
</Form.Item>} </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && <Form.Item )}
label="Bucket" {this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
name="bucket" <Form.Item label="Bucket" name="bucket">
>
<Input /> <Input />
</Form.Item>} </Form.Item>
{this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && <Form.Item )}
label="Token" {this.state.selectedVersion === InfluxDbVersion.INFLUXDB_2 && (
name="token" <Form.Item label="Token" name="token">
>
<Input.Password /> <Input.Password />
</Form.Item>} </Form.Item>
)}
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,44 +1,40 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import { Application, DeleteInfluxDbIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
Application,
DeleteInfluxDbIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class InfluxdbCard extends Component<IProps> { class InfluxdbCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteInfluxDbIntegrationRequest(); let req = new DeleteInfluxDbIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteInfluxDbIntegration(req, () => {}); ApplicationStore.deleteInfluxDbIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/influxdb/create"><PlusOutlined /></Link> <Link to="integrations/influxdb/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/influxdb/edit"><EditOutlined /></Link>, <Link to="integrations/influxdb/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +48,7 @@ class InfluxdbCard extends Component<IProps> {
cover={<img alt="InfluxDB" src="/integrations/influxdb.png" style={{ padding: 1 }} />} cover={<img alt="InfluxDB" src="/integrations/influxdb.png" style={{ padding: 1 }} />}
actions={actions} actions={actions}
> >
<Card.Meta <Card.Meta description="The InfluxDB integration writes events into an InfluxDB time-series database." />
description="The InfluxDB integration writes events into an InfluxDB time-series database."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -1,44 +1,40 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import { Application, DeleteLoraCloudIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
Application,
DeleteLoraCloudIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class LoRaCloudCard extends Component<IProps> { class LoRaCloudCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteLoraCloudIntegrationRequest(); let req = new DeleteLoraCloudIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteLoraCloudIntegration(req, () => {}); ApplicationStore.deleteLoraCloudIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/loracloud/create"><PlusOutlined /></Link> <Link to="integrations/loracloud/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/loracloud/edit"><EditOutlined /></Link>, <Link to="integrations/loracloud/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +48,7 @@ class LoRaCloudCard extends Component<IProps> {
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} actions={actions}
> >
<Card.Meta <Card.Meta description="The Semtech LoRa Cloud integration provides Modem & Geolocation Services." />
description="The Semtech LoRa Cloud integration provides Modem & Geolocation Services."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -7,13 +7,11 @@ import {
LoraCloudModemGeolocationServices, LoraCloudModemGeolocationServices,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb"; } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: LoraCloudIntegration; initialValues: LoraCloudIntegration;
onFinish: (obj: LoraCloudIntegration) => void; onFinish: (obj: LoraCloudIntegration) => void;
} }
interface IState { interface IState {
modemEnabled: boolean; modemEnabled: boolean;
geolocationTdoa: boolean; geolocationTdoa: boolean;
@ -22,7 +20,6 @@ interface IState {
geolocationGnss: boolean; geolocationGnss: boolean;
} }
class LoRaCloudIntegrationForm extends Component<IProps, IState> { class LoRaCloudIntegrationForm extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -56,7 +53,6 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
let mgs = new LoraCloudModemGeolocationServices(); let mgs = new LoraCloudModemGeolocationServices();
if (mgsv !== undefined) { if (mgsv !== undefined) {
mgs.setToken(mgsv.token); mgs.setToken(mgsv.token);
mgs.setModemEnabled(mgsv.modemEnabled); mgs.setModemEnabled(mgsv.modemEnabled);
@ -80,37 +76,37 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
i.setModemGeolocationServices(mgs); i.setModemGeolocationServices(mgs);
this.props.onFinish(i); this.props.onFinish(i);
} };
onModemEnabledChange = (v: boolean) => { onModemEnabledChange = (v: boolean) => {
this.setState({ this.setState({
modemEnabled: v, modemEnabled: v,
}); });
} };
onGeolocationTdoaChange = (v: boolean) => { onGeolocationTdoaChange = (v: boolean) => {
this.setState({ this.setState({
geolocationTdoa: v, geolocationTdoa: v,
}); });
} };
onGeolocationRssiChange = (v: boolean) => { onGeolocationRssiChange = (v: boolean) => {
this.setState({ this.setState({
geolocationRssi: v, geolocationRssi: v,
}); });
} };
onGeolocationWifiChange = (v: boolean) => { onGeolocationWifiChange = (v: boolean) => {
this.setState({ this.setState({
geolocationWifi: v, geolocationWifi: v,
}); });
} };
onGeolocationGnssChange = (v: boolean) => { onGeolocationGnssChange = (v: boolean) => {
this.setState({ this.setState({
geolocationGnss: v, geolocationGnss: v,
}); });
} };
render() { render() {
return ( return (
@ -132,38 +128,46 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
> >
<Switch onChange={this.onModemEnabledChange} /> <Switch onChange={this.onModemEnabledChange} />
</Form.Item> </Form.Item>
{this.state.modemEnabled && <Form.Item {this.state.modemEnabled && (
<Form.Item
label="GNSS port (FPort)" label="GNSS port (FPort)"
name={["modemGeolocationServices", "gnssPort"]} name={["modemGeolocationServices", "gnssPort"]}
tooltip="ChirpStack will only forward the FRMPayload for GNSS geolocation to LoRa Cloud when the uplink matches the configured port." 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!" }]} rules={[{ required: true, message: "Please enter a port number!" }]}
> >
<InputNumber min={0} max={255} /> <InputNumber min={0} max={255} />
</Form.Item>} </Form.Item>
{this.state.modemEnabled && <Form.Item )}
{this.state.modemEnabled && (
<Form.Item
label="Modem port (FPort)" label="Modem port (FPort)"
name={["modemGeolocationServices", "modemPort"]} name={["modemGeolocationServices", "modemPort"]}
tooltip="ChirpStack will only forward the FRMPayload to LoRa Cloud when the uplink matches the configured port." 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!" }]} rules={[{ required: true, message: "Please enter a port number!" }]}
> >
<InputNumber min={0} max={255} /> <InputNumber min={0} max={255} />
</Form.Item>} </Form.Item>
{this.state.modemEnabled && <Form.Item )}
{this.state.modemEnabled && (
<Form.Item
label="Use receive timestamp for GNSS geolocation" label="Use receive timestamp for GNSS geolocation"
name={["modemGeolocationServices", "gnssUseRxTime"]} 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." 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" valuePropName="checked"
> >
<Switch /> <Switch />
</Form.Item>} </Form.Item>
{this.state.modemEnabled && <Form.Item )}
{this.state.modemEnabled && (
<Form.Item
label="My device adheres to the LoRa Edge&trade; Tracker Reference Design protocol" label="My device adheres to the LoRa Edge&trade; Tracker Reference Design protocol"
name={["modemGeolocationServices", "parseTlv"]} name={["modemGeolocationServices", "parseTlv"]}
tooltip="If enabled, ChirpStack Application Server will try to resolve the location of the device if a geolocation payload is detected." tooltip="If enabled, ChirpStack Application Server will try to resolve the location of the device if a geolocation payload is detected."
valuePropName="checked" valuePropName="checked"
> >
<Switch /> <Switch />
</Form.Item>} </Form.Item>
)}
<Collapse style={{ marginBottom: 24 }}> <Collapse style={{ marginBottom: 24 }}>
<Collapse.Panel header="Advanced geolocation options" key={1}> <Collapse.Panel header="Advanced geolocation options" key={1}>
<Form.Item <Form.Item
@ -198,47 +202,59 @@ class LoRaCloudIntegrationForm extends Component<IProps, IState> {
> >
<Switch onChange={this.onGeolocationGnssChange} /> <Switch onChange={this.onGeolocationGnssChange} />
</Form.Item> </Form.Item>
{(this.state.geolocationTdoa || this.state.geolocationRssi) && <Form.Item {(this.state.geolocationTdoa || this.state.geolocationRssi) && (
<Form.Item
label="Geolocation buffer (TTL in seconds)" label="Geolocation buffer (TTL in seconds)"
name={["modemGeolocationServices", "geolocationBufferTtl"]} name={["modemGeolocationServices", "geolocationBufferTtl"]}
tooltip="The time in seconds that historical uplinks will be stored in the geolocation buffer. Used for TDOA and RSSI geolocation." 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} /> <InputNumber min={0} max={86400} />
</Form.Item>} </Form.Item>
{(this.state.geolocationTdoa || this.state.geolocationRssi) && <Form.Item )}
{(this.state.geolocationTdoa || this.state.geolocationRssi) && (
<Form.Item
label="Geolocation min buffer size" label="Geolocation min buffer size"
name={["modemGeolocationServices", "geolocationMinBufferSize"]} 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." 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} /> <InputNumber min={0} />
</Form.Item>} </Form.Item>
{this.state.geolocationWifi && <Form.Item )}
{this.state.geolocationWifi && (
<Form.Item
label="Wifi payload field" label="Wifi payload field"
name={["modemGeolocationServices", "geolocationWifiPayloadField"]} 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." 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 /> <Input />
</Form.Item>} </Form.Item>
{this.state.geolocationGnss && <Form.Item )}
{this.state.geolocationGnss && (
<Form.Item
label="GNSS payload field" label="GNSS payload field"
name={["modemGeolocationServices", "geolocationGnssPayloadField"]} name={["modemGeolocationServices", "geolocationGnssPayloadField"]}
tooltip="This must match the name of the field in the decoded payload which holds the LR1110 GNSS bytes." tooltip="This must match the name of the field in the decoded payload which holds the LR1110 GNSS bytes."
> >
<Input /> <Input />
</Form.Item>} </Form.Item>
{this.state.geolocationGnss && <Form.Item )}
{this.state.geolocationGnss && (
<Form.Item
label="Use receive timestamp for GNSS geolocation" label="Use receive timestamp for GNSS geolocation"
name={["modemGeolocationServices", "geolocationGnssUseRxTime"]} name={["modemGeolocationServices", "geolocationGnssUseRxTime"]}
valuePropName="checked" valuePropName="checked"
> >
<Switch /> <Switch />
</Form.Item>} </Form.Item>
)}
</Collapse.Panel> </Collapse.Panel>
</Collapse> </Collapse>
</Tabs.TabPane> </Tabs.TabPane>
</Tabs> </Tabs>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,23 +1,17 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card } from 'antd'; import { Col, Card } from "antd";
import {
Application,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
application: Application, application: Application;
} }
class HttpCard extends Component<IProps> { class HttpCard extends Component<IProps> {
render() { render() {
let actions: any[] = [ let actions: any[] = [<Link to="integrations/mqtt/certificate">Get certificate</Link>];
<Link to="integrations/mqtt/certificate">Get certificate</Link>
];
return ( return (
<Col span={8}> <Col span={8}>
@ -27,9 +21,7 @@ class HttpCard extends Component<IProps> {
cover={<img alt="MQTT" src="/integrations/mqtt.png" style={{ padding: 1 }} />} cover={<img alt="MQTT" src="/integrations/mqtt.png" style={{ padding: 1 }} />}
actions={actions} actions={actions}
> >
<Card.Meta <Card.Meta description="The MQTT integration forwards events to a MQTT broker." />
description="The MQTT integration forwards events to a MQTT broker."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -1,44 +1,40 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import { Application, DeleteMyDevicesIntegrationRequest } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
Application,
DeleteMyDevicesIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class MyDevicesCard extends Component<IProps> { class MyDevicesCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteMyDevicesIntegrationRequest(); let req = new DeleteMyDevicesIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteMyDevicesIntegration(req, () => {}); ApplicationStore.deleteMyDevicesIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/mydevices/create"><PlusOutlined /></Link> <Link to="integrations/mydevices/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/mydevices/edit"><EditOutlined /></Link>, <Link to="integrations/mydevices/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +48,7 @@ class MyDevicesCard extends Component<IProps> {
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} actions={actions}
> >
<Card.Meta <Card.Meta description="The myDevices integration forwards events to the myDevices platform." />
description="The myDevices integration forwards events to the myDevices platform."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -2,23 +2,18 @@ import React, { Component } from "react";
import { Form, Input, Button, Select } from "antd"; import { Form, Input, Button, Select } from "antd";
import { import { MyDevicesIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
MyDevicesIntegration,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: MyDevicesIntegration; initialValues: MyDevicesIntegration;
onFinish: (obj: MyDevicesIntegration) => void; onFinish: (obj: MyDevicesIntegration) => void;
} }
interface IState { interface IState {
selectedEndpoint: string; selectedEndpoint: string;
customEndpoint: string; customEndpoint: string;
} }
class MyDevicesIntegrationForm extends Component<IProps, IState> { class MyDevicesIntegrationForm extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -40,19 +35,19 @@ class MyDevicesIntegrationForm extends Component<IProps, IState> {
} }
this.props.onFinish(i); this.props.onFinish(i);
} };
onEndpointChange = (v: string) => { onEndpointChange = (v: string) => {
this.setState({ this.setState({
selectedEndpoint: v, selectedEndpoint: v,
}); });
} };
onCustomEndpointChange = (e: React.ChangeEvent<HTMLInputElement>) => { onCustomEndpointChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ this.setState({
customEndpoint: e.target.value, customEndpoint: e.target.value,
}); });
} };
render() { render() {
return ( return (
@ -64,19 +59,25 @@ class MyDevicesIntegrationForm extends Component<IProps, IState> {
> >
<Select onChange={this.onEndpointChange}> <Select onChange={this.onEndpointChange}>
<Select.Option value="https://lora.mydevices.com/v1/networks/chirpstackio/uplink">Cayenne</Select.Option> <Select.Option value="https://lora.mydevices.com/v1/networks/chirpstackio/uplink">Cayenne</Select.Option>
<Select.Option value="https://lora.iotinabox.com/v1/networks/iotinabox.chirpstackio/uplink">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.Option value="custom">Custom endpoint URL</Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
{this.state.selectedEndpoint === "custom" && <Form.Item {this.state.selectedEndpoint === "custom" && (
<Form.Item
label="myDevices API endpoint" label="myDevices API endpoint"
name="customEndpoint" name="customEndpoint"
rules={[{ required: true, message: "Please enter an API endpoint!" }]} rules={[{ required: true, message: "Please enter an API endpoint!" }]}
> >
<Input onChange={this.onCustomEndpointChange} /> <Input onChange={this.onCustomEndpointChange} />
</Form.Item>} </Form.Item>
)}
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,7 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import {
@ -11,34 +11,33 @@ import {
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class PilotThingsCard extends Component<IProps> { class PilotThingsCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeletePilotThingsIntegrationRequest(); let req = new DeletePilotThingsIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deletePilotThingsIntegration(req, () => {}); ApplicationStore.deletePilotThingsIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/pilot-things/create"><PlusOutlined /></Link> <Link to="integrations/pilot-things/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/pilot-things/edit"><EditOutlined /></Link>, <Link to="integrations/pilot-things/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +51,7 @@ class PilotThingsCard extends Component<IProps> {
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} actions={actions}
> >
<Card.Meta <Card.Meta description="The Pilot Things integration forwards messages to a Pilot Things instance." />
description="The Pilot Things integration forwards messages to a Pilot Things instance."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -2,18 +2,13 @@ import React, { Component } from "react";
import { Form, Input, Button } from "antd"; import { Form, Input, Button } from "antd";
import { import { PilotThingsIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
PilotThingsIntegration,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: PilotThingsIntegration; initialValues: PilotThingsIntegration;
onFinish: (obj: PilotThingsIntegration) => void; onFinish: (obj: PilotThingsIntegration) => void;
} }
class PilotThingsIntegrationForm extends Component<IProps> { class PilotThingsIntegrationForm extends Component<IProps> {
onFinish = (values: PilotThingsIntegration.AsObject) => { onFinish = (values: PilotThingsIntegration.AsObject) => {
const v = Object.assign(this.props.initialValues.toObject(), values); const v = Object.assign(this.props.initialValues.toObject(), values);
@ -24,7 +19,7 @@ class PilotThingsIntegrationForm extends Component<IProps> {
i.setToken(v.token); i.setToken(v.token);
this.props.onFinish(i); this.props.onFinish(i);
} };
render() { render() {
return ( return (
@ -44,7 +39,9 @@ class PilotThingsIntegrationForm extends Component<IProps> {
<Input.Password /> <Input.Password />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,44 +1,43 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Col, Card, Popconfirm } from 'antd'; import { Col, Card, Popconfirm } from "antd";
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
import { import {
Application, Application,
DeleteThingsBoardIntegrationRequest DeleteThingsBoardIntegrationRequest,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb"; } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import ApplicationStore from "../../../stores/ApplicationStore"; import ApplicationStore from "../../../stores/ApplicationStore";
interface IProps { interface IProps {
application: Application, application: Application;
add?: boolean; add?: boolean;
} }
class ThingsBoardCard extends Component<IProps> { class ThingsBoardCard extends Component<IProps> {
onDelete = () => { onDelete = () => {
let req = new DeleteThingsBoardIntegrationRequest(); let req = new DeleteThingsBoardIntegrationRequest();
req.setApplicationId(this.props.application.getId()); req.setApplicationId(this.props.application.getId());
ApplicationStore.deleteThingsBoardIntegration(req, () => {}); ApplicationStore.deleteThingsBoardIntegration(req, () => {});
} };
render() { render() {
let actions: any[] = []; let actions: any[] = [];
if (!!this.props.add) { if (!!this.props.add) {
actions = [ actions = [
<Link to="integrations/thingsboard/create"><PlusOutlined /></Link> <Link to="integrations/thingsboard/create">
<PlusOutlined />
</Link>,
]; ];
} else { } else {
actions = [ actions = [
<Link to="integrations/thingsboard/edit"><EditOutlined /></Link>, <Link to="integrations/thingsboard/edit">
<Popconfirm <EditOutlined />
title="Are you sure you want to delete this integration?" </Link>,
onConfirm={this.onDelete} <Popconfirm title="Are you sure you want to delete this integration?" onConfirm={this.onDelete}>
>
<DeleteOutlined /> <DeleteOutlined />
</Popconfirm>, </Popconfirm>,
]; ];
@ -52,9 +51,7 @@ class ThingsBoardCard extends Component<IProps> {
cover={<img alt="ThingsBoard" src="/integrations/thingsboard.png" style={{ padding: 1 }} />} cover={<img alt="ThingsBoard" src="/integrations/thingsboard.png" style={{ padding: 1 }} />}
actions={actions} actions={actions}
> >
<Card.Meta <Card.Meta description="The ThingsBoard integration forwards events to a ThingsBoard instance." />
description="The ThingsBoard integration forwards events to a ThingsBoard instance."
/>
</Card> </Card>
</Col> </Col>
); );

View File

@ -2,17 +2,13 @@ import React, { Component } from "react";
import { Form, Input, Button, Typography } from "antd"; import { Form, Input, Button, Typography } from "antd";
import { import { ThingsBoardIntegration } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
ThingsBoardIntegration,
} from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
interface IProps { interface IProps {
initialValues: ThingsBoardIntegration; initialValues: ThingsBoardIntegration;
onFinish: (obj: ThingsBoardIntegration) => void; onFinish: (obj: ThingsBoardIntegration) => void;
} }
class ThingsBoardIntegrationForm extends Component<IProps> { class ThingsBoardIntegrationForm extends Component<IProps> {
onFinish = (values: ThingsBoardIntegration.AsObject) => { onFinish = (values: ThingsBoardIntegration.AsObject) => {
const v = Object.assign(this.props.initialValues.toObject(), values); const v = Object.assign(this.props.initialValues.toObject(), values);
@ -22,7 +18,7 @@ class ThingsBoardIntegrationForm extends Component<IProps> {
i.setServer(v.server); i.setServer(v.server);
this.props.onFinish(i); this.props.onFinish(i);
} };
render() { render() {
return ( return (
@ -36,11 +32,14 @@ class ThingsBoardIntegrationForm extends Component<IProps> {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Typography.Paragraph> <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> </Typography.Paragraph>
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -1,7 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { presetPalettes } from '@ant-design/colors'; import { presetPalettes } from "@ant-design/colors";
import { Space, Breadcrumb, Card, Row, Col, PageHeader, Empty } from "antd"; import { Space, Breadcrumb, Card, Row, Col, PageHeader, Empty } from "antd";
import moment from "moment"; import moment from "moment";
@ -51,17 +51,15 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
items: resp.getResultList(), items: resp.getResultList(),
}); });
}); });
} };
render() { render() {
if (this.state.items.length === 0) { if (this.state.items.length === 0) {
return( return <Empty />;
<Empty />
);
} }
const boundsOptions: { const boundsOptions: {
padding: PointTuple, padding: PointTuple;
} = { } = {
padding: [50, 50], padding: [50, 50],
}; };
@ -79,7 +77,7 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
if (item.getLastSeenAt() !== undefined) { if (item.getLastSeenAt() !== undefined) {
let ts = moment(item.getLastSeenAt()!.toDate()); let ts = moment(item.getLastSeenAt()!.toDate());
lastSeen = ts.fromNow(); lastSeen = ts.fromNow();
if (ts.isBefore(moment().subtract(5, 'minutes'))) { if (ts.isBefore(moment().subtract(5, "minutes"))) {
color = "red"; color = "red";
} else { } else {
color = "green"; color = "green";
@ -89,11 +87,14 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
markers.push( markers.push(
<Marker position={pos} faIcon="wifi" color={color}> <Marker position={pos} faIcon="wifi" color={color}>
<Popup> <Popup>
<Link to={`/tenants/${item.getTenantId()}/gateways/${item.getGatewayId()}`}>{item.getName()}</Link><br /> <Link to={`/tenants/${item.getTenantId()}/gateways/${item.getGatewayId()}`}>{item.getName()}</Link>
{item.getGatewayId()}<br /><br /> <br />
{item.getGatewayId()}
<br />
<br />
{lastSeen} {lastSeen}
</Popup> </Popup>
</Marker> </Marker>,
); );
} }
@ -105,18 +106,18 @@ class GatewaysMap extends Component<{},GatewaysMapState> {
} }
} }
interface GatewayProps { interface GatewayProps {
summary?: GetGatewaysSummaryResponse; summary?: GetGatewaysSummaryResponse;
} }
class GatewaysActiveInactive extends Component<GatewayProps> { class GatewaysActiveInactive extends Component<GatewayProps> {
render() { render() {
if (this.props.summary === undefined || ( if (
this.props.summary.getNeverSeenCount() === 0 && this.props.summary === undefined ||
(this.props.summary.getNeverSeenCount() === 0 &&
this.props.summary.getInactiveCount() === 0 && this.props.summary.getInactiveCount() === 0 &&
this.props.summary.getActiveCount() === 0 this.props.summary.getActiveCount() === 0)
)) { ) {
return <Empty />; return <Empty />;
} }
@ -124,13 +125,13 @@ class GatewaysActiveInactive extends Component<GatewayProps> {
labels: ["Never seen", "Inactive", "Active"], labels: ["Never seen", "Inactive", "Active"],
datasets: [ datasets: [
{ {
data: [this.props.summary.getNeverSeenCount(), this.props.summary.getInactiveCount(), this.props.summary.getActiveCount()], data: [
backgroundColor: [ this.props.summary.getNeverSeenCount(),
presetPalettes.orange.primary, this.props.summary.getInactiveCount(),
presetPalettes.red.primary, this.props.summary.getActiveCount(),
presetPalettes.green.primary,
], ],
} backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
},
], ],
}; };
@ -140,24 +141,22 @@ class GatewaysActiveInactive extends Component<GatewayProps> {
animation: false, animation: false,
}; };
return( return <Doughnut data={data} options={options} className="chart-doughtnut" />;
<Doughnut data={data} options={options} className="chart-doughtnut" />
);
} }
} }
interface DeviceProps { interface DeviceProps {
summary?: GetDevicesSummaryResponse; summary?: GetDevicesSummaryResponse;
} }
class DevicesActiveInactive extends Component<DeviceProps> { class DevicesActiveInactive extends Component<DeviceProps> {
render() { render() {
if (this.props.summary === undefined || ( if (
this.props.summary.getNeverSeenCount() === 0 && this.props.summary === undefined ||
(this.props.summary.getNeverSeenCount() === 0 &&
this.props.summary.getInactiveCount() === 0 && this.props.summary.getInactiveCount() === 0 &&
this.props.summary.getActiveCount() === 0 this.props.summary.getActiveCount() === 0)
)) { ) {
return <Empty />; return <Empty />;
} }
@ -165,13 +164,13 @@ class DevicesActiveInactive extends Component<DeviceProps> {
labels: ["Never seen", "Inactive", "Active"], labels: ["Never seen", "Inactive", "Active"],
datasets: [ datasets: [
{ {
data: [this.props.summary.getNeverSeenCount(), this.props.summary.getInactiveCount(), this.props.summary.getActiveCount()], data: [
backgroundColor: [ this.props.summary.getNeverSeenCount(),
presetPalettes.orange.primary, this.props.summary.getInactiveCount(),
presetPalettes.red.primary, this.props.summary.getActiveCount(),
presetPalettes.green.primary,
], ],
} backgroundColor: [presetPalettes.orange.primary, presetPalettes.red.primary, presetPalettes.green.primary],
},
], ],
}; };
@ -181,18 +180,30 @@ class DevicesActiveInactive extends Component<DeviceProps> {
animation: false, 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> { class DevicesDataRates extends Component<DeviceProps> {
getColor = (dr: number) => { 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() { render() {
if (this.props.summary === undefined || this.props.summary.getDrCountMap().toArray().length === 0) { if (this.props.summary === undefined || this.props.summary.getDrCountMap().toArray().length === 0) {
@ -200,17 +211,19 @@ class DevicesDataRates extends Component<DeviceProps> {
} }
let data: { let data: {
labels: string[], labels: string[];
datasets: { datasets: {
data: number[], data: number[];
backgroundColor: string[], backgroundColor: string[];
}[], }[];
} = { } = {
labels: [], labels: [],
datasets: [{ datasets: [
{
data: [], data: [],
backgroundColor: [], backgroundColor: [],
}], },
],
}; };
for (const elm of this.props.summary.getDrCountMap().toArray()) { for (const elm of this.props.summary.getDrCountMap().toArray()) {
@ -225,13 +238,10 @@ class DevicesDataRates extends Component<DeviceProps> {
animation: false, animation: false,
}; };
return( return <Doughnut data={data} options={options} className="chart-doughtnut" />;
<Doughnut data={data} options={options} className="chart-doughtnut" />
);
} }
} }
interface IProps {} interface IProps {}
interface IState { interface IState {
@ -263,14 +273,16 @@ class Dashboard extends Component<IProps, IState> {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Network Server</span> <span>Network Server</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Dashboard</span> <span>Dashboard</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="Dashboard" title="Dashboard"
/> />
<Row gutter={24}> <Row gutter={24}>
@ -290,7 +302,9 @@ class Dashboard extends Component<IProps, IState> {
</Card> </Card>
</Col> </Col>
</Row> </Row>
<Card title="Gateway map"><GatewaysMap /></Card> <Card title="Gateway map">
<GatewaysMap />
</Card>
</Space> </Space>
); );
} }

View File

@ -4,18 +4,21 @@ import { Link, RouteComponentProps } from "react-router-dom";
import { Space, Breadcrumb, Card, PageHeader } from "antd"; import { Space, Breadcrumb, Card, PageHeader } from "antd";
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb"; import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
import { 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 { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import DeviceProfileForm from "./DeviceProfileForm"; import DeviceProfileForm from "./DeviceProfileForm";
import DeviceProfileStore from "../../stores/DeviceProfileStore"; import DeviceProfileStore from "../../stores/DeviceProfileStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
tenant: Tenant, tenant: Tenant;
} }
class CreateDeviceProfile extends Component<IProps> { class CreateDeviceProfile extends Component<IProps> {
onFinish = (obj: DeviceProfile) => { onFinish = (obj: DeviceProfile) => {
obj.setTenantId(this.props.tenant.getId()); obj.setTenantId(this.props.tenant.getId());
@ -26,7 +29,7 @@ class CreateDeviceProfile extends Component<IProps> {
DeviceProfileStore.create(req, (_resp: CreateDeviceProfileResponse) => { DeviceProfileStore.create(req, (_resp: CreateDeviceProfileResponse) => {
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`); this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
}); });
} };
render() { render() {
const codecScript = `// Decode uplink function. const codecScript = `// Decode uplink function.
@ -74,20 +77,26 @@ function encodeDownlink(input) {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Add</span> <span>Add</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="Add device profile" title="Add device profile"
/> />
<Card> <Card>

View File

@ -1,7 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Form, Input, Select, InputNumber, Switch, Row, Col, Button, Tabs } from "antd"; 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 { 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"; 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 DeviceProfileStore from "../../stores/DeviceProfileStore";
import CodeEditor from "../../components/CodeEditor"; import CodeEditor from "../../components/CodeEditor";
interface IProps { interface IProps {
initialValues: DeviceProfile; initialValues: DeviceProfile;
onFinish: (obj: DeviceProfile) => void; onFinish: (obj: DeviceProfile) => void;
@ -24,7 +23,6 @@ interface IState {
adrAlgorithms: [string, string][]; adrAlgorithms: [string, string][];
} }
class DeviceProfileForm extends Component<IProps, IState> { class DeviceProfileForm extends Component<IProps, IState> {
formRef = React.createRef<any>(); formRef = React.createRef<any>();
@ -101,51 +99,48 @@ class DeviceProfileForm extends Component<IProps, IState> {
} }
this.props.onFinish(dp); this.props.onFinish(dp);
} };
onSupportsOtaaChange = (checked: boolean) => { onSupportsOtaaChange = (checked: boolean) => {
this.setState({ this.setState({
supportsOtaa: checked, supportsOtaa: checked,
}); });
} };
onSupportsClassBChnage = (checked: boolean) => { onSupportsClassBChnage = (checked: boolean) => {
this.setState({ this.setState({
supportsClassB: checked, supportsClassB: checked,
}); });
} };
onSupportsClassCChange = (checked: boolean) => { onSupportsClassCChange = (checked: boolean) => {
this.setState({ this.setState({
supportsClassC: checked, supportsClassC: checked,
}); });
} };
onPayloadCodecRuntimeChange = (value: CodecRuntime) => { onPayloadCodecRuntimeChange = (value: CodecRuntime) => {
this.setState({ this.setState({
payloadCodecRuntime: value, payloadCodecRuntime: value,
}); });
} };
render() { 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 ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}> <Form
layout="vertical"
initialValues={this.props.initialValues.toObject()}
onFinish={this.onFinish}
ref={this.formRef}
>
<Tabs> <Tabs>
<Tabs.TabPane tab="General" key="1"> <Tabs.TabPane tab="General" key="1">
<Form.Item <Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
label="Name"
name="name"
rules={[{required: true, message: "Please enter a name!"}]}
>
<Input disabled={this.props.disabled} /> <Input disabled={this.props.disabled} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label="Region" name="region" rules={[{ required: true, message: "Please select a region!" }]}>
label="Region"
name="region"
rules={[{required: true, message: "Please select a region!"}]}
>
<Select disabled={this.props.disabled}> <Select disabled={this.props.disabled}>
<Select.Option value={Region.AS923}>AS923</Select.Option> <Select.Option value={Region.AS923}>AS923</Select.Option>
<Select.Option value={Region.AS923_2}>AS923-2</Select.Option> <Select.Option value={Region.AS923_2}>AS923-2</Select.Option>
@ -204,9 +199,7 @@ class DeviceProfileForm extends Component<IProps, IState> {
name="adrAlgorithmId" 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}> <Select disabled={this.props.disabled}>{adrOptions}</Select>
{adrOptions}
</Select>
</Form.Item> </Form.Item>
<Row gutter={24}> <Row gutter={24}>
<Col span={8}> <Col span={8}>
@ -241,14 +234,11 @@ class DeviceProfileForm extends Component<IProps, IState> {
</Row> </Row>
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="Join (OTAA / ABP)" key="2"> <Tabs.TabPane tab="Join (OTAA / ABP)" key="2">
<Form.Item <Form.Item label="Device supports OTAA" name="supportsOtaa" valuePropName="checked">
label="Device supports OTAA"
name="supportsOtaa"
valuePropName="checked"
>
<Switch onChange={this.onSupportsOtaaChange} disabled={this.props.disabled} /> <Switch onChange={this.onSupportsOtaaChange} disabled={this.props.disabled} />
</Form.Item> </Form.Item>
{!this.state.supportsOtaa && <Row> {!this.state.supportsOtaa && (
<Row>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
label="RX1 delay" label="RX1 delay"
@ -268,8 +258,10 @@ class DeviceProfileForm extends Component<IProps, IState> {
<InputNumber min={0} max={15} disabled={this.props.disabled} /> <InputNumber min={0} max={15} disabled={this.props.disabled} />
</Form.Item> </Form.Item>
</Col> </Col>
</Row>} </Row>
{!this.state.supportsOtaa && <Row> )}
{!this.state.supportsOtaa && (
<Row>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
label="RX2 data-rate" label="RX2 data-rate"
@ -289,41 +281,38 @@ class DeviceProfileForm extends Component<IProps, IState> {
<InputNumber min={0} style={{ width: "200px" }} disabled={this.props.disabled} /> <InputNumber min={0} style={{ width: "200px" }} disabled={this.props.disabled} />
</Form.Item> </Form.Item>
</Col> </Col>
</Row>} </Row>
)}
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="Class-B" key="3"> <Tabs.TabPane tab="Class-B" key="3">
<Form.Item <Form.Item label="Device supports Class-B" name="supportsClassB" valuePropName="checked">
label="Device supports Class-B"
name="supportsClassB"
valuePropName="checked"
>
<Switch onChange={this.onSupportsClassBChnage} disabled={this.props.disabled} /> <Switch onChange={this.onSupportsClassBChnage} disabled={this.props.disabled} />
</Form.Item> </Form.Item>
{this.state.supportsClassB && <Form.Item {this.state.supportsClassB && (
<Form.Item
label="Class-B confirmed downlink timeout (seconds)" label="Class-B confirmed downlink timeout (seconds)"
tooltip="Class-B timeout (in seconds) for confirmed downlink transmissions." tooltip="Class-B timeout (in seconds) for confirmed downlink transmissions."
name="classBTimeout" name="classBTimeout"
rules={[{ required: true, message: "Please enter a Class-B confirmed downlink timeout!" }]} rules={[{ required: true, message: "Please enter a Class-B confirmed downlink timeout!" }]}
> >
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item>} </Form.Item>
)}
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="Class-C" key="4"> <Tabs.TabPane tab="Class-C" key="4">
<Form.Item <Form.Item label="Device supports Class-C" name="supportsClassC" valuePropName="checked">
label="Device supports Class-C"
name="supportsClassC"
valuePropName="checked"
>
<Switch onChange={this.onSupportsClassCChange} disabled={this.props.disabled} /> <Switch onChange={this.onSupportsClassCChange} disabled={this.props.disabled} />
</Form.Item> </Form.Item>
{this.state.supportsClassC && <Form.Item {this.state.supportsClassC && (
<Form.Item
label="Class-C confirmed downlink timeout (seconds)" label="Class-C confirmed downlink timeout (seconds)"
tooltip="Class-C timeout (in seconds) for confirmed downlink transmissions." tooltip="Class-C timeout (in seconds) for confirmed downlink transmissions."
name="classCTimeout" name="classCTimeout"
rules={[{ required: true, message: "Please enter a Class-C confirmed downlink timeout!" }]} rules={[{ required: true, message: "Please enter a Class-C confirmed downlink timeout!" }]}
> >
<InputNumber min={0} disabled={this.props.disabled} /> <InputNumber min={0} disabled={this.props.disabled} />
</Form.Item>} </Form.Item>
)}
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="Codec" key="5"> <Tabs.TabPane tab="Codec" key="5">
<Form.Item <Form.Item
@ -355,7 +344,7 @@ class DeviceProfileForm extends Component<IProps, IState> {
{...restField} {...restField}
name={[name, 0]} name={[name, 0]}
fieldKey={[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} /> <Input placeholder="Key" disabled={this.props.disabled} />
</Form.Item> </Form.Item>
@ -365,7 +354,7 @@ class DeviceProfileForm extends Component<IProps, IState> {
{...restField} {...restField}
name={[name, 1]} name={[name, 1]}
fieldKey={[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} /> <Input placeholder="Value" disabled={this.props.disabled} />
</Form.Item> </Form.Item>
@ -376,7 +365,13 @@ class DeviceProfileForm extends Component<IProps, IState> {
</Row> </Row>
))} ))}
<Form.Item> <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 Add tag
</Button> </Button>
</Form.Item> </Form.Item>
@ -386,7 +381,9 @@ class DeviceProfileForm extends Component<IProps, IState> {
</Tabs.TabPane> </Tabs.TabPane>
</Tabs> </Tabs>
<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.Item>
</Form> </Form>
); );

View File

@ -4,7 +4,13 @@ import { RouteComponentProps, Link } from "react-router-dom";
import { Space, Breadcrumb, Card, Button, PageHeader } from "antd"; import { Space, Breadcrumb, Card, Button, PageHeader } from "antd";
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; 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 DeviceProfileForm from "./DeviceProfileForm";
import DeviceProfileStore from "../../stores/DeviceProfileStore"; import DeviceProfileStore from "../../stores/DeviceProfileStore";
@ -12,7 +18,6 @@ import SessionStore from "../../stores/SessionStore";
import DeleteConfirm from "../../components/DeleteConfirm"; import DeleteConfirm from "../../components/DeleteConfirm";
import Admin from "../../components/Admin"; import Admin from "../../components/Admin";
interface IState { interface IState {
deviceProfile?: DeviceProfile; deviceProfile?: DeviceProfile;
} }
@ -25,7 +30,6 @@ interface IProps extends RouteComponentProps<MatchParams> {
tenant: Tenant; tenant: Tenant;
} }
class EditDeviceProfile extends Component<IProps, IState> { class EditDeviceProfile extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -46,7 +50,7 @@ class EditDeviceProfile extends Component<IProps, IState> {
deviceProfile: resp.getDeviceProfile(), deviceProfile: resp.getDeviceProfile(),
}); });
}); });
} };
onFinish = (obj: DeviceProfile) => { onFinish = (obj: DeviceProfile) => {
let req = new UpdateDeviceProfileRequest(); let req = new UpdateDeviceProfileRequest();
@ -55,7 +59,7 @@ class EditDeviceProfile extends Component<IProps, IState> {
DeviceProfileStore.update(req, () => { DeviceProfileStore.update(req, () => {
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`); this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
}); });
} };
deleteDeviceProfile = () => { deleteDeviceProfile = () => {
let req = new DeleteDeviceProfileRequest(); let req = new DeleteDeviceProfileRequest();
@ -64,7 +68,7 @@ class EditDeviceProfile extends Component<IProps, IState> {
DeviceProfileStore.delete(req, () => { DeviceProfileStore.delete(req, () => {
this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`); this.props.history.push(`/tenants/${this.props.tenant.getId()}/device-profiles`);
}); });
} };
render() { render() {
const dp = this.state.deviceProfile; const dp = this.state.deviceProfile;
@ -73,37 +77,45 @@ class EditDeviceProfile extends Component<IProps, IState> {
return null; 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 ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>{dp.getName()}</span> <span>{dp.getName()}</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title={dp.getName()} title={dp.getName()}
subTitle={`device profile id: ${dp.getId()}`} subTitle={`device profile id: ${dp.getId()}`}
extra={[ extra={[
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin> <Admin tenantId={this.props.tenant.getId()} isDeviceAdmin>
<DeleteConfirm <DeleteConfirm typ="device profile" confirm={dp.getName()} onConfirm={this.deleteDeviceProfile}>
typ="device profile" <Button danger type="primary">
confirm={dp.getName()} Delete device profile
onConfirm={this.deleteDeviceProfile} </Button>
>
<Button danger type="primary">Delete device profile</Button>
</DeleteConfirm> </DeleteConfirm>
</Admin> </Admin>,
]} ]}
/> />
<Card> <Card>

View File

@ -4,7 +4,11 @@ import { Link } from "react-router-dom";
import { Space, Breadcrumb, Button, PageHeader } from "antd"; import { Space, Breadcrumb, Button, PageHeader } from "antd";
import { ColumnsType } from "antd/es/table"; 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 { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import { Region } from "@chirpstack/chirpstack-api-grpc-web/common/common_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 DeviceProfileStore from "../../stores/DeviceProfileStore";
import Admin from "../../components/Admin"; import Admin from "../../components/Admin";
interface IProps { interface IProps {
tenant: Tenant; tenant: Tenant;
} }
class ListDeviceProfiles extends Component<IProps> { class ListDeviceProfiles extends Component<IProps> {
columns = (): ColumnsType<DeviceProfileListItem.AsObject> => { columns = (): ColumnsType<DeviceProfileListItem.AsObject> => {
return [ return [
@ -97,7 +99,7 @@ class ListDeviceProfiles extends Component<IProps> {
}, },
}, },
]; ];
} };
getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => { getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
let req = new ListDeviceProfilesRequest(); let req = new ListDeviceProfilesRequest();
@ -109,35 +111,37 @@ class ListDeviceProfiles extends Component<IProps> {
const obj = resp.toObject(); const obj = resp.toObject();
callbackFunc(obj.totalCount, obj.resultList); callbackFunc(obj.totalCount, obj.resultList);
}); });
} };
render() { render() {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Device profiles</span> <span>Device profiles</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="Device profiles" title="Device profiles"
extra={[ extra={[
<Admin tenantId={this.props.tenant.getId()} isDeviceAdmin> <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> <Button type="primary">
</Admin> <Link to={`/tenants/${this.props.tenant.getId()}/device-profiles/create`}>Add device profile</Link>
</Button>
</Admin>,
]} ]}
/> />
<DataTable <DataTable columns={this.columns()} getPage={this.getPage} rowKey="id" />
columns={this.columns()}
getPage={this.getPage}
rowKey="id"
/>
</Space> </Space>
); );
} }

View File

@ -6,19 +6,20 @@ import { Space, Breadcrumb, Card, PageHeader } from "antd";
import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb"; import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import { CreateDeviceRequest, Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_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 DeviceForm from "./DeviceForm";
import DeviceStore from "../../stores/DeviceStore"; import DeviceStore from "../../stores/DeviceStore";
import DeviceProfileStore from "../../stores/DeviceProfileStore"; import DeviceProfileStore from "../../stores/DeviceProfileStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
tenant: Tenant; tenant: Tenant;
application: Application; application: Application;
} }
class CreateDevice extends Component<IProps> { class CreateDevice extends Component<IProps> {
onFinish = (obj: Device) => { onFinish = (obj: Device) => {
obj.setApplicationId(this.props.application.getId()); obj.setApplicationId(this.props.application.getId());
@ -33,13 +34,17 @@ class CreateDevice extends Component<IProps> {
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => { DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
let dp = resp.getDeviceProfile()!; let dp = resp.getDeviceProfile()!;
if (dp.getSupportsOtaa()) { if (dp.getSupportsOtaa()) {
this.props.history.push(`/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 { } 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() { render() {
let device = new Device(); let device = new Device();
@ -48,23 +53,33 @@ class CreateDevice extends Component<IProps> {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<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>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Add device</span> <span>Add device</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title="Add device" title="Add device"
/> />
<Card> <Card>

View File

@ -19,14 +19,12 @@ import AesKeyInput from "../../components/AesKeyInput";
import DevAddrInput from "../../components/DevAddrInput"; import DevAddrInput from "../../components/DevAddrInput";
import DeviceStore from "../../stores/DeviceStore"; import DeviceStore from "../../stores/DeviceStore";
interface FormProps { interface FormProps {
initialValues: DeviceActivationPb; initialValues: DeviceActivationPb;
device: Device; device: Device;
onFinish: (obj: DeviceActivationPb) => void; onFinish: (obj: DeviceActivationPb) => void;
} }
class LW10DeviceActivationForm extends Component<FormProps> { class LW10DeviceActivationForm extends Component<FormProps> {
formRef = React.createRef<any>(); formRef = React.createRef<any>();
@ -44,11 +42,16 @@ class LW10DeviceActivationForm extends Component<FormProps> {
da.setNFCntDown(v.nFCntDown); da.setNFCntDown(v.nFCntDown);
this.props.onFinish(da); this.props.onFinish(da);
} };
render() { render() {
return ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}> <Form
layout="vertical"
initialValues={this.props.initialValues.toObject()}
onFinish={this.onFinish}
ref={this.formRef}
>
<DevAddrInput <DevAddrInput
label="Device address" label="Device address"
name="devAddr" name="devAddr"
@ -73,31 +76,26 @@ class LW10DeviceActivationForm extends Component<FormProps> {
/> />
<Row gutter={24}> <Row gutter={24}>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item label="Uplink frame-counter" name="fCntUp">
label="Uplink frame-counter"
name="fCntUp"
>
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item label="Downlink frame-counter" name="nFCntDown">
label="Downlink frame-counter"
name="nFCntDown"
>
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">(Re)activate device</Button> <Button type="primary" htmlType="submit">
(Re)activate device
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );
} }
} }
class LW11DeviceActivationForm extends Component<FormProps> { class LW11DeviceActivationForm extends Component<FormProps> {
formRef = React.createRef<any>(); formRef = React.createRef<any>();
@ -115,11 +113,16 @@ class LW11DeviceActivationForm extends Component<FormProps> {
da.setNFCntDown(v.nFCntDown); da.setNFCntDown(v.nFCntDown);
this.props.onFinish(da); this.props.onFinish(da);
} };
render() { render() {
return ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}> <Form
layout="vertical"
initialValues={this.props.initialValues.toObject()}
onFinish={this.onFinish}
ref={this.formRef}
>
<DevAddrInput <DevAddrInput
label="Device address" label="Device address"
name="devAddr" name="devAddr"
@ -158,39 +161,31 @@ class LW11DeviceActivationForm extends Component<FormProps> {
/> />
<Row gutter={24}> <Row gutter={24}>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item label="Uplink frame-counter" name="fCntUp">
label="Uplink frame-counter"
name="fCntUp"
>
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item label="Downlink frame-counter (network)" name="nFCntDown">
label="Downlink frame-counter (network)"
name="nFCntDown"
>
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item label="Downlink frame-counter (application)" name="aFCntDown">
label="Downlink frame-counter (application)"
name="aFCntDown"
>
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">(Re)activate device</Button> <Button type="primary" htmlType="submit">
(Re)activate device
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );
} }
} }
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
tenant: Tenant; tenant: Tenant;
application: Application; application: Application;
@ -198,13 +193,11 @@ interface IProps extends RouteComponentProps {
deviceProfile: DeviceProfile; deviceProfile: DeviceProfile;
} }
interface IState { interface IState {
deviceActivation?: DeviceActivationPb; deviceActivation?: DeviceActivationPb;
deviceActivationRequested: boolean; deviceActivationRequested: boolean;
} }
class DeviceActivation extends Component<IProps, IState> { class DeviceActivation extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -231,9 +224,11 @@ class DeviceActivation extends Component<IProps, IState> {
req.setDeviceActivation(obj); req.setDeviceActivation(obj);
DeviceStore.activate(req, () => { 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() { render() {
if (!this.state.deviceActivationRequested) { if (!this.state.deviceActivationRequested) {
@ -250,8 +245,12 @@ class DeviceActivation extends Component<IProps, IState> {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
{!lw11 && <LW10DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} /> } {!lw11 && (
{lw11 && <LW11DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} /> } <LW10DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} />
)}
{lw11 && (
<LW11DeviceActivationForm initialValues={initialValues} device={this.props.device} onFinish={this.onFinish} />
)}
</Space> </Space>
); );
} }

View File

@ -12,14 +12,11 @@ import {
GetDeviceStatsRequest, GetDeviceStatsRequest,
GetDeviceStatsResponse, GetDeviceStatsResponse,
} from "@chirpstack/chirpstack-api-grpc-web/api/device_pb"; } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
import { import { DeviceProfile } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
DeviceProfile,
} from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
import DeviceStore from "../../stores/DeviceStore"; import DeviceStore from "../../stores/DeviceStore";
import Heatmap from "../../components/Heatmap"; import Heatmap from "../../components/Heatmap";
interface IProps { interface IProps {
device: Device; device: Device;
deviceProfile: DeviceProfile; deviceProfile: DeviceProfile;
@ -32,7 +29,7 @@ interface IState {
statsUpFreq: HeatmapStats[]; statsUpFreq: HeatmapStats[];
statsUpDr?: any; statsUpDr?: any;
statsGwRssi?: any; statsGwRssi?: any;
statsGwSnr?: any, statsGwSnr?: any;
} }
interface HeatmapStats { interface HeatmapStats {
@ -78,7 +75,7 @@ class DeviceDashboard extends Component<IProps, IState> {
lineTension: number; lineTension: number;
pointBackgroundColor: string; pointBackgroundColor: string;
data: number[]; data: number[];
}[], }[];
} = { } = {
labels: [], labels: [],
datasets: [ datasets: [
@ -129,28 +126,32 @@ class DeviceDashboard extends Component<IProps, IState> {
let statsGwRssiData: (number | null)[] = []; let statsGwRssiData: (number | null)[] = [];
let statsGwRssi = { let statsGwRssi = {
labels: statsGwRssiLabels, labels: statsGwRssiLabels,
datasets: [{ datasets: [
{
label: "rssi (reported by gateways)", label: "rssi (reported by gateways)",
borderColor: "rgba(33, 150, 243, 1)", borderColor: "rgba(33, 150, 243, 1)",
backgroundColor: "rgba(0, 0, 0, 0)", backgroundColor: "rgba(0, 0, 0, 0)",
lineTension: 0, lineTension: 0,
pointBackgroundColor: "rgba(33, 150, 243, 1)", pointBackgroundColor: "rgba(33, 150, 243, 1)",
data: statsGwRssiData, data: statsGwRssiData,
}], },
],
}; };
let statsGwSnrLabels: string[] = []; let statsGwSnrLabels: string[] = [];
let statsGwSnrData: (number | null)[] = []; let statsGwSnrData: (number | null)[] = [];
let statsGwSnr = { let statsGwSnr = {
labels: statsGwSnrLabels, labels: statsGwSnrLabels,
datasets: [{ datasets: [
{
label: "rssi (reported by gateways)", label: "rssi (reported by gateways)",
borderColor: "rgba(33, 150, 243, 1)", borderColor: "rgba(33, 150, 243, 1)",
backgroundColor: "rgba(0, 0, 0, 0)", backgroundColor: "rgba(0, 0, 0, 0)",
lineTension: 0, lineTension: 0,
pointBackgroundColor: "rgba(33, 150, 243, 1)", pointBackgroundColor: "rgba(33, 150, 243, 1)",
data: statsGwSnrData, data: statsGwSnrData,
}], },
],
}; };
let statsUpFreq: HeatmapStats[] = []; let statsUpFreq: HeatmapStats[] = [];
@ -160,7 +161,10 @@ class DeviceDashboard extends Component<IProps, IState> {
statsUpFreq.push({ statsUpFreq.push({
x: moment(row.getTime()!.toDate()).format("YYYY-MM-DD"), 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")); 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]); statsErrorsSet[v[0]].push(v[1]);
} }
for (const v of row.getRxPacketsPerDrMap().toObject()) { 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]) => { Object.entries(statsErrorsSet).forEach(([k, v]) => {
statsErrors.datasets.push({ statsErrors.datasets.push({
label: k, 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]) => { Object.entries(statsUpDrSet).forEach(([k, v]) => {
statsUpDr.datasets.push({ statsUpDr.datasets.push({
label: k, label: k,
@ -231,8 +266,7 @@ class DeviceDashboard extends Component<IProps, IState> {
statsGwSnr: statsGwSnr, statsGwSnr: statsGwSnr,
}); });
}); });
} };
render() { render() {
const animation: false = false; const animation: false = false;
@ -292,7 +326,13 @@ class DeviceDashboard extends Component<IProps, IState> {
<Card> <Card>
<Descriptions> <Descriptions>
<Descriptions.Item label="Last seen">{lastSeenAt}</Descriptions.Item> <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="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="Enabled">{this.props.device.getIsDisabled() ? "no" : "yes"}</Descriptions.Item>
<Descriptions.Item label="Description">{this.props.device.getDescription()}</Descriptions.Item> <Descriptions.Item label="Description">{this.props.device.getDescription()}</Descriptions.Item>
</Descriptions> </Descriptions>

View File

@ -6,14 +6,13 @@ import { StreamDeviceEventsRequest, LogItem } from "@chirpstack/chirpstack-api-g
import InternalStore from "../../stores/InternalStore"; import InternalStore from "../../stores/InternalStore";
import LogTable from "../../components/LogTable"; import LogTable from "../../components/LogTable";
interface IProps { interface IProps {
device: Device; device: Device;
} }
interface IState { interface IState {
events: LogItem[]; events: LogItem[];
cancelFunc?: () => void, cancelFunc?: () => void;
} }
class DeviceEvents extends Component<IProps, IState> { class DeviceEvents extends Component<IProps, IState> {
@ -44,7 +43,7 @@ class DeviceEvents extends Component<IProps, IState> {
this.setState({ this.setState({
cancelFunc: cancelFunc, cancelFunc: cancelFunc,
}); });
} };
onMessage = (l: LogItem) => { onMessage = (l: LogItem) => {
let events = this.state.events; let events = this.state.events;
@ -55,10 +54,10 @@ class DeviceEvents extends Component<IProps, IState> {
events: events, events: events,
}); });
} }
} };
render() { render() {
return(<LogTable logs={this.state.events} />); return <LogTable logs={this.state.events} />;
} }
} }

View File

@ -1,18 +1,22 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Form, Input, Row, Col, Button, Tabs } from "antd"; 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 { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import { Device } from "@chirpstack/chirpstack-api-grpc-web/api/device_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 EuiInput from "../../components/EuiInput";
import { OptionsCallbackFunc, OptionCallbackFunc } from "../../components/Autocomplete"; import { OptionsCallbackFunc, OptionCallbackFunc } from "../../components/Autocomplete";
import AutocompleteInput from "../../components/AutocompleteInput"; import AutocompleteInput from "../../components/AutocompleteInput";
import DeviceProfileStore from "../../stores/DeviceProfileStore"; import DeviceProfileStore from "../../stores/DeviceProfileStore";
interface IProps { interface IProps {
tenant: Tenant; tenant: Tenant;
initialValues: Device; initialValues: Device;
@ -20,7 +24,6 @@ interface IProps {
update?: boolean; update?: boolean;
} }
class DeviceForm extends Component<IProps> { class DeviceForm extends Component<IProps> {
formRef = React.createRef<any>(); formRef = React.createRef<any>();
@ -45,7 +48,7 @@ class DeviceForm extends Component<IProps> {
} }
this.props.onFinish(d); this.props.onFinish(d);
} };
getDeviceProfileOptions = (search: string, fn: OptionsCallbackFunc) => { getDeviceProfileOptions = (search: string, fn: OptionsCallbackFunc) => {
let req = new ListDeviceProfilesRequest(); let req = new ListDeviceProfilesRequest();
@ -54,10 +57,12 @@ class DeviceForm extends Component<IProps> {
req.setLimit(10); req.setLimit(10);
DeviceProfileStore.list(req, (resp: ListDeviceProfilesResponse) => { 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); fn(options);
}); });
} };
getDeviceProfileOption = (id: string, fn: OptionCallbackFunc) => { getDeviceProfileOption = (id: string, fn: OptionCallbackFunc) => {
let req = new GetDeviceProfileRequest(); let req = new GetDeviceProfileRequest();
@ -69,24 +74,22 @@ class DeviceForm extends Component<IProps> {
fn({ label: dp.getName(), value: dp.getId() }); fn({ label: dp.getName(), value: dp.getId() });
} }
}); });
} };
render() { render() {
return ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}> <Form
layout="vertical"
initialValues={this.props.initialValues.toObject()}
onFinish={this.onFinish}
ref={this.formRef}
>
<Tabs> <Tabs>
<Tabs.TabPane tab="Device" key="1"> <Tabs.TabPane tab="Device" key="1">
<Form.Item <Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
label="Name"
name="name"
rules={[{required: true, message: "Please enter a name!"}]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label="Description" name="description">
label="Description"
name="description"
>
<Input.TextArea /> <Input.TextArea />
</Form.Item> </Form.Item>
<EuiInput <EuiInput
@ -117,7 +120,7 @@ class DeviceForm extends Component<IProps> {
{...restField} {...restField}
name={[name, 0]} name={[name, 0]}
fieldKey={[name, 0]} fieldKey={[name, 0]}
rules={[{ required: true, message: 'Please enter a key!' }]} rules={[{ required: true, message: "Please enter a key!" }]}
> >
<Input placeholder="Key" /> <Input placeholder="Key" />
</Form.Item> </Form.Item>
@ -127,7 +130,7 @@ class DeviceForm extends Component<IProps> {
{...restField} {...restField}
name={[name, 1]} name={[name, 1]}
fieldKey={[name, 1]} fieldKey={[name, 1]}
rules={[{ required: true, message: 'Please enter a value!' }]} rules={[{ required: true, message: "Please enter a value!" }]}
> >
<Input placeholder="Value" /> <Input placeholder="Value" />
</Form.Item> </Form.Item>
@ -157,7 +160,7 @@ class DeviceForm extends Component<IProps> {
{...restField} {...restField}
name={[name, 0]} name={[name, 0]}
fieldKey={[name, 0]} fieldKey={[name, 0]}
rules={[{ required: true, message: 'Please enter a key!' }]} rules={[{ required: true, message: "Please enter a key!" }]}
> >
<Input placeholder="Key" /> <Input placeholder="Key" />
</Form.Item> </Form.Item>
@ -167,7 +170,7 @@ class DeviceForm extends Component<IProps> {
{...restField} {...restField}
name={[name, 1]} name={[name, 1]}
fieldKey={[name, 1]} fieldKey={[name, 1]}
rules={[{ required: true, message: 'Please enter a value!' }]} rules={[{ required: true, message: "Please enter a value!" }]}
> >
<Input placeholder="Value" /> <Input placeholder="Value" />
</Form.Item> </Form.Item>
@ -188,7 +191,9 @@ class DeviceForm extends Component<IProps> {
</Tabs.TabPane> </Tabs.TabPane>
</Tabs> </Tabs>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );

View File

@ -6,14 +6,13 @@ import { StreamDeviceFramesRequest, LogItem } from "@chirpstack/chirpstack-api-g
import InternalStore from "../../stores/InternalStore"; import InternalStore from "../../stores/InternalStore";
import LogTable from "../../components/LogTable"; import LogTable from "../../components/LogTable";
interface IProps { interface IProps {
device: Device; device: Device;
} }
interface IState { interface IState {
frames: LogItem[]; frames: LogItem[];
cancelFunc?: () => void, cancelFunc?: () => void;
} }
class DeviceFrames extends Component<IProps, IState> { class DeviceFrames extends Component<IProps, IState> {
@ -44,7 +43,7 @@ class DeviceFrames extends Component<IProps, IState> {
this.setState({ this.setState({
cancelFunc: cancelFunc, cancelFunc: cancelFunc,
}); });
} };
onMessage = (l: LogItem) => { onMessage = (l: LogItem) => {
let frames = this.state.frames; let frames = this.state.frames;
@ -55,10 +54,10 @@ class DeviceFrames extends Component<IProps, IState> {
frames: frames, frames: frames,
}); });
} }
} };
render() { render() {
return(<LogTable logs={this.state.frames} />); return <LogTable logs={this.state.frames} />;
} }
} }

View File

@ -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 { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb"; import { Application } from "@chirpstack/chirpstack-api-grpc-web/api/application_pb";
import { DeviceProfile, GetDeviceProfileRequest, GetDeviceProfileResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb"; import {
import { Device, GetDeviceRequest, GetDeviceResponse, DeleteDeviceRequest } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb"; 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 DeviceStore from "../../stores/DeviceStore";
import DeviceProfileStore from "../../stores/DeviceProfileStore"; import DeviceProfileStore from "../../stores/DeviceProfileStore";
@ -20,7 +29,6 @@ import DeviceEvents from "./DeviceEvents";
import DeviceQueue from "./DeviceQueue"; import DeviceQueue from "./DeviceQueue";
import DeviceActivation from "./DeviceActivation"; import DeviceActivation from "./DeviceActivation";
interface MatchParams { interface MatchParams {
devEui: string; devEui: string;
} }
@ -36,7 +44,6 @@ interface IState {
lastSeenAt?: Date; lastSeenAt?: Date;
} }
class DeviceLayout extends Component<IProps, IState> { class DeviceLayout extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -52,9 +59,12 @@ class DeviceLayout extends Component<IProps, IState> {
req.setDevEui(this.props.match.params.devEui); req.setDevEui(this.props.match.params.devEui);
DeviceStore.get(req, (resp: GetDeviceResponse) => { DeviceStore.get(req, (resp: GetDeviceResponse) => {
this.setState({ this.setState(
{
device: resp.getDevice(), device: resp.getDevice(),
}, cb); },
cb,
);
if (resp.getLastSeenAt() !== undefined) { if (resp.getLastSeenAt() !== undefined) {
this.setState({ this.setState({
@ -62,7 +72,7 @@ class DeviceLayout extends Component<IProps, IState> {
}); });
} }
}); });
} };
getDeviceProfile = () => { getDeviceProfile = () => {
let req = new GetDeviceProfileRequest(); let req = new GetDeviceProfileRequest();
@ -73,7 +83,7 @@ class DeviceLayout extends Component<IProps, IState> {
deviceProfile: resp.getDeviceProfile(), deviceProfile: resp.getDeviceProfile(),
}); });
}); });
} };
deleteDevice = () => { deleteDevice = () => {
let req = new DeleteDeviceRequest(); let req = new DeleteDeviceRequest();
@ -82,7 +92,7 @@ class DeviceLayout extends Component<IProps, IState> {
DeviceStore.delete(req, () => { DeviceStore.delete(req, () => {
this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`); this.props.history.push(`/tenants/${this.props.tenant.getId()}/applications/${this.props.application.getId()}`);
}); });
} };
render() { render() {
const device = this.state.device; const device = this.state.device;
@ -119,56 +129,132 @@ class DeviceLayout extends Component<IProps, IState> {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<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>
<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>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>{device.getName()}</span> <span>{device.getName()}</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
title={device.getName()} title={device.getName()}
subTitle={`device eui: ${device.getDevEui()}`} subTitle={`device eui: ${device.getDevEui()}`}
extra={[ extra={[
<DeleteConfirm <DeleteConfirm typ="device" confirm={device.getName()} onConfirm={this.deleteDevice}>
typ="device" <Button danger type="primary">
confirm={device.getName()} Delete device
onConfirm={this.deleteDevice} </Button>
> </DeleteConfirm>,
<Button danger type="primary">Delete device</Button>
</DeleteConfirm>
]} ]}
/> />
<Card> <Card>
<Menu mode="horizontal" selectedKeys={[tab]} style={{ marginBottom: 24 }}> <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="dashboard">
<Menu.Item key="edit"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/edit`}>Configuration</Link></Menu.Item> <Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}`}>
<Menu.Item key="keys" disabled={!dp.getSupportsOtaa()}><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/keys`}>OTAA keys</Link></Menu.Item> Dashboard
<Menu.Item key="activation"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/activation`}>Activation</Link></Menu.Item> </Link>
<Menu.Item key="queue"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/queue`}>Queue</Link></Menu.Item> </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="edit">
<Menu.Item key="frames"><Link to={`/tenants/${tenant.getId()}/applications/${app.getId()}/devices/${device.getDevEui()}/frames`}>LoRaWAN frames</Link></Menu.Item> <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> </Menu>
<Switch> <Switch>
<Route exact path={this.props.match.path} render={props => <DeviceDashboard device={device} lastSeenAt={this.state.lastSeenAt} deviceProfile={dp} {...props} />} /> <Route
<Route exact path={`${this.props.match.path}/edit`} render={props => <EditDevice device={device} application={app} tenant={tenant} {...props} />} /> exact
<Route exact path={`${this.props.match.path}/keys`} render={props => <SetDeviceKeys device={device} application={app} tenant={tenant} deviceProfile={dp} {...props} />} /> path={this.props.match.path}
<Route exact path={`${this.props.match.path}/frames`} render={props => <DeviceFrames device={device} {...props} /> } /> render={props => (
<Route exact path={`${this.props.match.path}/events`} render={props => <DeviceEvents device={device} {...props} /> } /> <DeviceDashboard device={device} lastSeenAt={this.state.lastSeenAt} deviceProfile={dp} {...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} /> } /> />
<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> </Switch>
</Card> </Card>
</Space> </Space>

View File

@ -13,13 +13,13 @@ import {
GetDeviceQueueItemsRequest, GetDeviceQueueItemsRequest,
GetDeviceQueueItemsResponse, GetDeviceQueueItemsResponse,
FlushDeviceQueueRequest, 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 DataTable, { GetPageCallbackFunc } from "../../components/DataTable";
import DeviceStore from "../../stores/DeviceStore"; import DeviceStore from "../../stores/DeviceStore";
import CodeEditor from "../../components/CodeEditor"; import CodeEditor from "../../components/CodeEditor";
interface IProps { interface IProps {
device: Device; device: Device;
} }
@ -28,7 +28,6 @@ interface IState {
refreshCounter: number; refreshCounter: number;
} }
class DeviceQueue extends Component<IProps, IState> { class DeviceQueue extends Component<IProps, IState> {
formRef = React.createRef<any>(); formRef = React.createRef<any>();
@ -97,11 +96,11 @@ class DeviceQueue extends Component<IProps, IState> {
dataIndex: "data", dataIndex: "data",
key: "data", key: "data",
render: (text, record) => { 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) => { getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
let req = new GetDeviceQueueItemsRequest(); let req = new GetDeviceQueueItemsRequest();
@ -111,13 +110,13 @@ class DeviceQueue extends Component<IProps, IState> {
const obj = resp.toObject(); const obj = resp.toObject();
callbackFunc(obj.totalCount, obj.resultList); callbackFunc(obj.totalCount, obj.resultList);
}); });
} };
refreshQueue = () => { refreshQueue = () => {
this.setState({ this.setState({
refreshCounter: this.state.refreshCounter + 1, refreshCounter: this.state.refreshCounter + 1,
}); });
} };
flushQueue = () => { flushQueue = () => {
let req = new FlushDeviceQueueRequest(); let req = new FlushDeviceQueueRequest();
@ -125,7 +124,7 @@ class DeviceQueue extends Component<IProps, IState> {
DeviceStore.flushQueue(req, () => { DeviceStore.flushQueue(req, () => {
this.refreshQueue(); this.refreshQueue();
}); });
} };
onEnqueue = (values: any) => { onEnqueue = (values: any) => {
let req = new EnqueueDeviceQueueItemRequest(); let req = new EnqueueDeviceQueueItemRequest();
@ -136,11 +135,11 @@ class DeviceQueue extends Component<IProps, IState> {
item.setConfirmed(values.confirmed); item.setConfirmed(values.confirmed);
if (values.hex !== undefined) { if (values.hex !== undefined) {
item.setData(Buffer.from(values.hex, 'hex')); item.setData(Buffer.from(values.hex, "hex"));
} }
if (values.base64 !== undefined) { if (values.base64 !== undefined) {
item.setData(Buffer.from(values.base64, 'base64')); item.setData(Buffer.from(values.base64, "base64"));
} }
if (values.json !== undefined) { if (values.json !== undefined) {
@ -162,11 +161,11 @@ class DeviceQueue extends Component<IProps, IState> {
req.setItem(item); req.setItem(item);
DeviceStore.enqueue(req, (_) => { DeviceStore.enqueue(req, _ => {
this.formRef.current.resetFields(); this.formRef.current.resetFields();
this.refreshQueue(); this.refreshQueue();
}); });
} };
render() { render() {
return ( return (
@ -195,24 +194,20 @@ class DeviceQueue extends Component<IProps, IState> {
</Form.Item> </Form.Item>
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="JSON" key="3"> <Tabs.TabPane tab="JSON" key="3">
<CodeEditor <CodeEditor name="json" value="{}" formRef={this.formRef} />
name="json"
value="{}"
formRef={this.formRef}
/>
</Tabs.TabPane> </Tabs.TabPane>
</Tabs> </Tabs>
<Button type="primary" htmlType="submit">Enqueue</Button> <Button type="primary" htmlType="submit">
Enqueue
</Button>
</Form> </Form>
</Card> </Card>
<Row justify="end"> <Row justify="end">
<Space direction="horizontal" size="large"> <Space direction="horizontal" size="large">
<Button icon={<RedoOutlined />} onClick={this.refreshQueue}>Reload</Button> <Button icon={<RedoOutlined />} onClick={this.refreshQueue}>
<Popconfirm Reload
title="Are you sure you want to flush the queue?" </Button>
placement="left" <Popconfirm title="Are you sure you want to flush the queue?" placement="left" onConfirm={this.flushQueue}>
onConfirm={this.flushQueue}
>
<Button icon={<DeleteOutlined />}>Flush queue</Button> <Button icon={<DeleteOutlined />}>Flush queue</Button>
</Popconfirm> </Popconfirm>
</Space> </Space>

View File

@ -14,21 +14,20 @@ interface IProps extends RouteComponentProps {
device: Device; device: Device;
} }
class EditDevice extends Component<IProps> { class EditDevice extends Component<IProps> {
onFinish = (obj: Device) => { onFinish = (obj: Device) => {
let req = new UpdateDeviceRequest(); let req = new UpdateDeviceRequest();
req.setDevice(obj); req.setDevice(obj);
DeviceStore.update(req, () => { DeviceStore.update(req, () => {
this.props.history.push(`/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() { render() {
return( return <DeviceForm initialValues={this.props.device} onFinish={this.onFinish} tenant={this.props.tenant} update />;
<DeviceForm initialValues={this.props.device} onFinish={this.onFinish} tenant={this.props.tenant} update />
);
} }
} }

View File

@ -5,10 +5,20 @@ import moment from "moment";
import { Space, Button, Dropdown, Menu, Modal, Select } from "antd"; import { Space, Button, Dropdown, Menu, Modal, Select } from "antd";
import { ColumnsType } from "antd/es/table"; import { ColumnsType } from "antd/es/table";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 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 { 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 { import {
ListMulticastGroupsRequest, ListMulticastGroupsRequest,
ListMulticastGroupsResponse, ListMulticastGroupsResponse,
@ -21,20 +31,17 @@ import DeviceStore from "../../stores/DeviceStore";
import MulticastGroupStore from "../../stores/MulticastGroupStore"; import MulticastGroupStore from "../../stores/MulticastGroupStore";
import Admin from "../../components/Admin"; import Admin from "../../components/Admin";
interface IProps { interface IProps {
application: Application; application: Application;
} }
interface IState { interface IState {
selectedRowIds: string[]; selectedRowIds: string[];
multicastGroups: MulticastGroupListItem[]; multicastGroups: MulticastGroupListItem[];
mgModalVisible: boolean; mgModalVisible: boolean;
mgSelected: string, mgSelected: string;
} }
class ListDevices extends Component<IProps, IState> { class ListDevices extends Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -80,7 +87,13 @@ class ListDevices extends Component<IProps, IState> {
key: "devEui", key: "devEui",
width: 250, width: 250,
render: (text, record) => ( 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", dataIndex: "deviceProfileName",
key: "deviceProfileName", key: "deviceProfileName",
render: (text, record) => ( 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) => { getPage = (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => {
let req = new ListDevicesRequest(); let req = new ListDevicesRequest();
@ -131,31 +146,31 @@ class ListDevices extends Component<IProps, IState> {
const obj = resp.toObject(); const obj = resp.toObject();
callbackFunc(obj.totalCount, obj.resultList); callbackFunc(obj.totalCount, obj.resultList);
}); });
} };
onRowsSelectChange = (ids: string[]) => { onRowsSelectChange = (ids: string[]) => {
this.setState({ this.setState({
selectedRowIds: ids, selectedRowIds: ids,
}); });
} };
showMgModal = () => { showMgModal = () => {
this.setState({ this.setState({
mgModalVisible: true, mgModalVisible: true,
}); });
} };
hideMgModal = () => { hideMgModal = () => {
this.setState({ this.setState({
mgModalVisible: false, mgModalVisible: false,
}); });
} };
onMgSelected = (value: string) => { onMgSelected = (value: string) => {
this.setState({ this.setState({
mgSelected: value, mgSelected: value,
}); });
} };
handleMgModalOk = () => { handleMgModalOk = () => {
for (let devEui of this.state.selectedRowIds) { for (let devEui of this.state.selectedRowIds) {
@ -169,7 +184,7 @@ class ListDevices extends Component<IProps, IState> {
this.setState({ this.setState({
mgModalVisible: false, mgModalVisible: false,
}); });
} };
render() { render() {
const menu = ( const menu = (
@ -178,11 +193,19 @@ class ListDevices extends Component<IProps, IState> {
</Menu> </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 ( return (
<Space direction="vertical" size="large" style={{ width: "100%" }}> <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 === ""}}> <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%" }}> <Space direction="vertical" size="large" style={{ width: "100%" }}>
<Select style={{ width: "100%" }} onChange={this.onMgSelected} placeholder="Select Multicast-group"> <Select style={{ width: "100%" }} onChange={this.onMgSelected} placeholder="Select Multicast-group">
{mgOptions} {mgOptions}
@ -191,8 +214,21 @@ class ListDevices extends Component<IProps, IState> {
</Modal> </Modal>
<Admin tenantId={this.props.application.getTenantId()} isDeviceAdmin> <Admin tenantId={this.props.application.getTenantId()} isDeviceAdmin>
<Space direction="horizontal" style={{ float: "right" }}> <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> <Button type="primary">
<Dropdown placement="bottomRight" overlay={menu} trigger={['click']} disabled={this.state.selectedRowIds.length === 0}><Button>Selected devices</Button></Dropdown> <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> </Space>
</Admin> </Admin>
<DataTable <DataTable

View File

@ -20,13 +20,11 @@ import { MacVersion } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb
import AesKeyInput from "../../components/AesKeyInput"; import AesKeyInput from "../../components/AesKeyInput";
import DeviceStore from "../../stores/DeviceStore"; import DeviceStore from "../../stores/DeviceStore";
interface FormProps { interface FormProps {
initialValues: DeviceKeys; initialValues: DeviceKeys;
onFinish: (obj: DeviceKeys) => void; onFinish: (obj: DeviceKeys) => void;
} }
class LW10DeviceKeysForm extends Component<FormProps> { class LW10DeviceKeysForm extends Component<FormProps> {
formRef = React.createRef<any>(); formRef = React.createRef<any>();
@ -40,11 +38,16 @@ class LW10DeviceKeysForm extends Component<FormProps> {
dk.setNwkKey(v.nwkKey); dk.setNwkKey(v.nwkKey);
this.props.onFinish(dk); this.props.onFinish(dk);
} };
render() { render() {
return ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}> <Form
layout="vertical"
initialValues={this.props.initialValues.toObject()}
onFinish={this.onFinish}
ref={this.formRef}
>
<AesKeyInput <AesKeyInput
label="Application key" label="Application key"
name="nwkKey" name="nwkKey"
@ -54,14 +57,15 @@ class LW10DeviceKeysForm extends Component<FormProps> {
required required
/> />
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );
} }
} }
class LW11DeviceKeysForm extends Component<FormProps> { class LW11DeviceKeysForm extends Component<FormProps> {
formRef = React.createRef<any>(); formRef = React.createRef<any>();
@ -74,11 +78,16 @@ class LW11DeviceKeysForm extends Component<FormProps> {
dk.setNwkKey(v.nwkKey); dk.setNwkKey(v.nwkKey);
this.props.onFinish(dk); this.props.onFinish(dk);
} };
render() { render() {
return ( return (
<Form layout="vertical" initialValues={this.props.initialValues.toObject()} onFinish={this.onFinish} ref={this.formRef}> <Form
layout="vertical"
initialValues={this.props.initialValues.toObject()}
onFinish={this.onFinish}
ref={this.formRef}
>
<AesKeyInput <AesKeyInput
label="Application key" label="Application key"
tooltip="For LoRaWAN 1.1 devices. In case your device does not support LoRaWAN 1.1, update the device-profile first." tooltip="For LoRaWAN 1.1 devices. In case your device does not support LoRaWAN 1.1, update the device-profile first."
@ -96,14 +105,15 @@ class LW11DeviceKeysForm extends Component<FormProps> {
required required
/> />
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit">Submit</Button> <Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item> </Form.Item>
</Form> </Form>
); );
} }
} }
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
tenant: Tenant; tenant: Tenant;
application: Application; application: Application;
@ -144,7 +154,7 @@ class SetDeviceKeys extends Component<IProps, IState> {
}); });
} }
}); });
} };
onFinish = (obj: DeviceKeys) => { onFinish = (obj: DeviceKeys) => {
if (this.state.deviceKeys) { if (this.state.deviceKeys) {
@ -153,9 +163,10 @@ class SetDeviceKeys extends Component<IProps, IState> {
req.setDeviceKeys(obj); req.setDeviceKeys(obj);
DeviceStore.updateKeys(req, () => { DeviceStore.updateKeys(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()}`,
);
}); });
} else { } else {
// this is a create // this is a create
let req = new CreateDeviceKeysRequest(); let req = new CreateDeviceKeysRequest();
@ -163,16 +174,18 @@ class SetDeviceKeys extends Component<IProps, IState> {
req.setDeviceKeys(obj); req.setDeviceKeys(obj);
DeviceStore.createKeys(req, () => { DeviceStore.createKeys(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()}`,
);
}); });
} }
} };
flushDevNonces = () => { flushDevNonces = () => {
let req = new FlushDevNoncesRequest(); let req = new FlushDevNoncesRequest();
req.setDevEui(this.props.device.getDevEui()); req.setDevEui(this.props.device.getDevEui());
DeviceStore.flushDevNonces(req, () => {}); DeviceStore.flushDevNonces(req, () => {});
} };
render() { render() {
if (!this.state.deviceKeysRequested) { if (!this.state.deviceKeysRequested) {
@ -189,7 +202,8 @@ class SetDeviceKeys extends Component<IProps, IState> {
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
{this.state.deviceKeys && <div style={{float: "right"}}> {this.state.deviceKeys && (
<div style={{ float: "right" }}>
<Popconfirm <Popconfirm
placement="left" placement="left"
title="Are you sure you want to flush all device-nonces that have been used during previous OTAA activations?" title="Are you sure you want to flush all device-nonces that have been used during previous OTAA activations?"
@ -197,7 +211,8 @@ class SetDeviceKeys extends Component<IProps, IState> {
> >
<Button>Flush OTAA device nonces</Button> <Button>Flush OTAA device nonces</Button>
</Popconfirm> </Popconfirm>
</div>} </div>
)}
{!lw11 && <LW10DeviceKeysForm initialValues={initialValues} onFinish={this.onFinish} />} {!lw11 && <LW10DeviceKeysForm initialValues={initialValues} onFinish={this.onFinish} />}
{lw11 && <LW11DeviceKeysForm initialValues={initialValues} onFinish={this.onFinish} />} {lw11 && <LW11DeviceKeysForm initialValues={initialValues} onFinish={this.onFinish} />}
</Space> </Space>

View File

@ -9,12 +9,10 @@ import { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import GatewayForm from "./GatewayForm"; import GatewayForm from "./GatewayForm";
import GatewayStore from "../../stores/GatewayStore"; import GatewayStore from "../../stores/GatewayStore";
interface IProps extends RouteComponentProps { interface IProps extends RouteComponentProps {
tenant: Tenant; tenant: Tenant;
} }
class CreateGateway extends Component<IProps> { class CreateGateway extends Component<IProps> {
onFinish = (obj: Gateway) => { onFinish = (obj: Gateway) => {
obj.setTenantId(this.props.tenant.getId()); obj.setTenantId(this.props.tenant.getId());
@ -25,7 +23,7 @@ class CreateGateway extends Component<IProps> {
GatewayStore.create(req, () => { GatewayStore.create(req, () => {
this.props.history.push(`/tenants/${this.props.tenant.getId()}/gateways/${obj.getGatewayId()}`); this.props.history.push(`/tenants/${this.props.tenant.getId()}/gateways/${obj.getGatewayId()}`);
}); });
} };
render() { render() {
const gateway = new Gateway(); const gateway = new Gateway();
@ -34,20 +32,26 @@ class CreateGateway extends Component<IProps> {
<Space direction="vertical" style={{ width: "100%" }} size="large"> <Space direction="vertical" style={{ width: "100%" }} size="large">
<PageHeader <PageHeader
title="Add gateway" title="Add gateway"
breadcrumbRender={() => <Breadcrumb> breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Tenants</span> <span>Tenants</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<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>
<Breadcrumb.Item> <Breadcrumb.Item>
<span><Link to={`/tenants/${this.props.tenant.getId()}/gateways`}>Gateways</Link></span> <span>
<Link to={`/tenants/${this.props.tenant.getId()}/gateways`}>Gateways</Link>
</span>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item> <Breadcrumb.Item>
<span>Add</span> <span>Add</span>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb>} </Breadcrumb>
)}
/> />
<Card> <Card>
<GatewayForm initialValues={gateway} onFinish={this.onFinish} /> <GatewayForm initialValues={gateway} onFinish={this.onFinish} />

Some files were not shown because too many files have changed in this diff Show More