mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-16 06:18:27 +00:00
Implement automatic formatting for JS / TSX source code.
Implemented automatic code formatting for JS/TSX using prettier and husky with pre-commit hook Signed-off-by: SAGAR PATEL <sagar.a.patel@slscorp.com> Co-authored-by: Orne Brocaar <info@brocaar.com>
This commit is contained in:
@ -2,7 +2,6 @@ import { Component } from "react";
|
||||
|
||||
import SessionStore from "../stores/SessionStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
tenantId?: string;
|
||||
isDeviceAdmin?: boolean;
|
||||
@ -68,14 +67,14 @@ class Admin extends Component<IProps, IState> {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.admin) {
|
||||
return(this.props.children);
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
return(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@ import { Input, Select, Button, Space, Form } from "antd";
|
||||
import { ReloadOutlined } from "@ant-design/icons";
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -18,7 +18,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class AesKeyInput extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -39,7 +38,7 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value) {
|
||||
@ -51,7 +50,7 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
|
||||
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let v = e.target.value;
|
||||
const match = v.match(/[A-Fa-f0-9]/g);
|
||||
const match = v.match(/[A-Fa-f0-9]/g);
|
||||
|
||||
let value = "";
|
||||
if (match) {
|
||||
@ -62,10 +61,13 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: value,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
onByteOrderSelect = (v: string) => {
|
||||
if (v === this.state.byteOrder) {
|
||||
@ -79,21 +81,27 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
const current = this.state.value;
|
||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||
|
||||
this.setState({
|
||||
value: bytes.reverse().join(""),
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: bytes.reverse().join(""),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
generateRandom = () => {
|
||||
let cryptoObj = window.crypto || window.Crypto;
|
||||
let b = new Uint8Array(16);
|
||||
cryptoObj.getRandomValues(b);
|
||||
|
||||
let key = Buffer.from(b).toString('hex');
|
||||
this.setState({
|
||||
value: key,
|
||||
}, this.updateField);
|
||||
}
|
||||
let key = Buffer.from(b).toString("hex");
|
||||
this.setState(
|
||||
{
|
||||
value: key,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const addon = (
|
||||
@ -102,23 +110,34 @@ class AesKeyInput extends Component<IProps, IState> {
|
||||
<Select.Option value="msb">MSB</Select.Option>
|
||||
<Select.Option value="lsb">LSB</Select.Option>
|
||||
</Select>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
||||
<ReloadOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{32}/g),
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{32}/g),
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
tooltip={this.props.tooltip}
|
||||
>
|
||||
<Input hidden />
|
||||
<Input.Password id={`${this.props.name}Render`} onChange={this.onChange} addonAfter={!this.props.disabled && addon} style={{fontFamily: "monospace"}} value={this.state.value} disabled={this.props.disabled} />
|
||||
<Input.Password
|
||||
id={`${this.props.name}Render`}
|
||||
onChange={this.onChange}
|
||||
addonAfter={!this.props.disabled && addon}
|
||||
style={{ fontFamily: "monospace" }}
|
||||
value={this.state.value}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
@ -2,24 +2,23 @@ import React, { Component } from "react";
|
||||
|
||||
import { Select } from "antd";
|
||||
|
||||
export type OptionsCallbackFunc = (o: {label: string, value: string}[]) => void;
|
||||
export type OptionCallbackFunc = (o: {label: string, value: string}) => void;
|
||||
export type OptionsCallbackFunc = (o: { label: string; value: string }[]) => void;
|
||||
export type OptionCallbackFunc = (o: { label: string; value: string }) => void;
|
||||
|
||||
interface IProps {
|
||||
placeholder: string;
|
||||
className: string;
|
||||
value?: string,
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void,
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void,
|
||||
onSelect?: (s: string) => void,
|
||||
value?: string;
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void;
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void;
|
||||
onSelect?: (s: string) => void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
option?: {label: string, value: string};
|
||||
options: {label: string, value: string}[];
|
||||
option?: { label: string; value: string };
|
||||
options: { label: string; value: string }[];
|
||||
}
|
||||
|
||||
|
||||
class Autocomplete extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -31,7 +30,7 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value && this.props.value !== "") {
|
||||
this.props.getOption(this.props.value, (o: {label: string, value: string}) => {
|
||||
this.props.getOption(this.props.value, (o: { label: string; value: string }) => {
|
||||
this.setState({
|
||||
options: [o],
|
||||
});
|
||||
@ -45,7 +44,7 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
if (this.props.value && this.props.value !== "") {
|
||||
this.props.getOption(this.props.value, (o: {label: string, value: string}) => {
|
||||
this.props.getOption(this.props.value, (o: { label: string; value: string }) => {
|
||||
this.setState({
|
||||
options: [o],
|
||||
});
|
||||
@ -54,7 +53,7 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
onFocus = () => {
|
||||
this.props.getOptions("", (options) => {
|
||||
this.props.getOptions("", options => {
|
||||
if (this.state.option !== undefined) {
|
||||
const selected = this.state.option.value;
|
||||
|
||||
@ -67,30 +66,30 @@ class Autocomplete extends Component<IProps, IState> {
|
||||
options: options,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSearch = (value: string) => {
|
||||
this.props.getOptions(value, (options) => {
|
||||
this.props.getOptions(value, options => {
|
||||
this.setState({
|
||||
options: options,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSelect = (value: string, option: any) => {
|
||||
this.setState({
|
||||
option: {label: option.label, value: option.value},
|
||||
option: { label: option.label, value: option.value },
|
||||
});
|
||||
|
||||
if (this.props.onSelect !== undefined) {
|
||||
this.props.onSelect(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { getOption, getOptions, ...otherProps } = this.props;
|
||||
|
||||
return(
|
||||
return (
|
||||
<Select
|
||||
showSearch
|
||||
options={this.state.options}
|
||||
|
@ -4,26 +4,26 @@ import { Form } from "antd";
|
||||
|
||||
import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "./Autocomplete";
|
||||
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void,
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void,
|
||||
getOption: (s: string, fn: OptionCallbackFunc) => void;
|
||||
getOptions: (s: string, fn: OptionsCallbackFunc) => void;
|
||||
}
|
||||
|
||||
|
||||
class AutocompleteInput extends Component<IProps> {
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please select a ${this.props.label}`,
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please select a ${this.props.label}`,
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
>
|
||||
|
@ -1,15 +1,14 @@
|
||||
import React, { Component } from "react";
|
||||
import {Controlled as CodeMirror} from "react-codemirror2";
|
||||
import { Controlled as CodeMirror } from "react-codemirror2";
|
||||
|
||||
import { Form } from "antd";
|
||||
|
||||
import "codemirror/mode/javascript/javascript";
|
||||
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label?: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label?: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -20,7 +19,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class CodeEditor extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -43,13 +41,16 @@ class CodeEditor extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = (editor: any, data: any, newCode: string) => {
|
||||
this.setState({
|
||||
value: newCode,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: newCode,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const codeMirrorOptions = {
|
||||
@ -60,21 +61,13 @@ class CodeEditor extends Component<IProps, IState> {
|
||||
};
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
tooltip={this.props.tooltip}
|
||||
>
|
||||
<div style={{border: "1px solid #cccccc"}}>
|
||||
<CodeMirror
|
||||
value={this.state.value}
|
||||
options={codeMirrorOptions}
|
||||
onBeforeChange={this.handleChange}
|
||||
/>
|
||||
<Form.Item label={this.props.label} name={this.props.name} tooltip={this.props.tooltip}>
|
||||
<div style={{ border: "1px solid #cccccc" }}>
|
||||
<CodeMirror value={this.state.value} options={codeMirrorOptions} onBeforeChange={this.handleChange} />
|
||||
</div>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CodeEditor
|
||||
export default CodeEditor;
|
||||
|
@ -5,10 +5,8 @@ import { ColumnsType } from "antd/es/table";
|
||||
|
||||
import SessionStore from "../stores/SessionStore";
|
||||
|
||||
|
||||
export type GetPageCallbackFunc = (totalCount: number, rows: object[]) => void;
|
||||
|
||||
|
||||
interface IProps {
|
||||
columns: ColumnsType<any>;
|
||||
getPage: (limit: number, offset: number, callbackFunc: GetPageCallbackFunc) => void;
|
||||
@ -23,10 +21,9 @@ interface IState {
|
||||
pageSize: number;
|
||||
currentPage: number;
|
||||
rows: object[];
|
||||
loading: boolean,
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
|
||||
class DataTable extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -53,37 +50,40 @@ class DataTable extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
onChangePage = (page: number, pageSize?: number | void) => {
|
||||
this.setState({
|
||||
loading: true,
|
||||
}, () => {
|
||||
let pz = pageSize;
|
||||
if (!pz) {
|
||||
pz = this.state.pageSize;
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
loading: true,
|
||||
},
|
||||
() => {
|
||||
let pz = pageSize;
|
||||
if (!pz) {
|
||||
pz = this.state.pageSize;
|
||||
}
|
||||
|
||||
this.props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => {
|
||||
this.setState({
|
||||
currentPage: page,
|
||||
totalCount: totalCount,
|
||||
rows: rows,
|
||||
pageSize: pz || 0,
|
||||
loading: false,
|
||||
this.props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => {
|
||||
this.setState({
|
||||
currentPage: page,
|
||||
totalCount: totalCount,
|
||||
rows: rows,
|
||||
pageSize: pz || 0,
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
onShowSizeChange = (page: number, pageSize: number) => {
|
||||
this.onChangePage(page, pageSize);
|
||||
SessionStore.setRowsPerPage(pageSize);
|
||||
}
|
||||
};
|
||||
|
||||
onRowsSelectChange = (ids: React.Key[]) => {
|
||||
const idss = ids as string[];
|
||||
if (this.props.onRowsSelectChange) {
|
||||
this.props.onRowsSelectChange(idss);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { getPage, refreshKey, ...otherProps } = this.props;
|
||||
@ -97,12 +97,12 @@ class DataTable extends Component<IProps, IState> {
|
||||
let pagination = undefined;
|
||||
if (this.props.noPagination === undefined || this.props.noPagination === false) {
|
||||
pagination = {
|
||||
current: this.state.currentPage,
|
||||
total: this.state.totalCount,
|
||||
pageSize: this.state.pageSize,
|
||||
onChange: this.onChangePage,
|
||||
showSizeChanger: true,
|
||||
onShowSizeChange: this.onShowSizeChange,
|
||||
current: this.state.currentPage,
|
||||
total: this.state.totalCount,
|
||||
pageSize: this.state.pageSize,
|
||||
onChange: this.onChangePage,
|
||||
showSizeChanger: true,
|
||||
onShowSizeChange: this.onShowSizeChange,
|
||||
};
|
||||
}
|
||||
|
||||
@ -113,8 +113,7 @@ class DataTable extends Component<IProps, IState> {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return(
|
||||
return (
|
||||
<Table
|
||||
loading={loadingProps}
|
||||
dataSource={this.state.rows}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
import { Popover, Button, Typography, Space, Input } from 'antd';
|
||||
|
||||
import { Popover, Button, Typography, Space, Input } from "antd";
|
||||
|
||||
interface IProps {
|
||||
typ: string;
|
||||
@ -9,17 +8,15 @@ interface IProps {
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
|
||||
interface ConfirmState {
|
||||
confirm: string;
|
||||
}
|
||||
|
||||
|
||||
class DeleteConfirmContent extends Component<IProps, ConfirmState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
confirm: '',
|
||||
confirm: "",
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,24 +24,31 @@ class DeleteConfirmContent extends Component<IProps, ConfirmState> {
|
||||
this.setState({
|
||||
confirm: e.target.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return(
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<Typography.Text>Enter '{this.props.confirm}' to confirm you want to delete this {this.props.typ}:</Typography.Text>
|
||||
<Input placeholder={this.props.confirm} onChange={this.onChange} />
|
||||
<Button onClick={this.props.onConfirm} disabled={this.state.confirm !== this.props.confirm} style={{float: "right"}}>Delete</Button>
|
||||
<Typography.Text>
|
||||
Enter '{this.props.confirm}' to confirm you want to delete this {this.props.typ}:
|
||||
</Typography.Text>
|
||||
<Input placeholder={this.props.confirm} onChange={this.onChange} />
|
||||
<Button
|
||||
onClick={this.props.onConfirm}
|
||||
disabled={this.state.confirm !== this.props.confirm}
|
||||
style={{ float: "right" }}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DeleteConfirm extends Component<IProps> {
|
||||
render() {
|
||||
return(
|
||||
<Popover content={<DeleteConfirmContent {...this.props}/>} trigger="click" placement="left">
|
||||
return (
|
||||
<Popover content={<DeleteConfirmContent {...this.props} />} trigger="click" placement="left">
|
||||
{this.props.children}
|
||||
</Popover>
|
||||
);
|
||||
|
@ -3,21 +3,15 @@ import React, { Component } from "react";
|
||||
import { Input, Select, Button, Space, Form } from "antd";
|
||||
import { ReloadOutlined } from "@ant-design/icons";
|
||||
|
||||
|
||||
import {
|
||||
GetRandomDevAddrRequest,
|
||||
GetRandomDevAddrResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
|
||||
import { GetRandomDevAddrRequest, GetRandomDevAddrResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
||||
|
||||
import DeviceStore from "../stores/DeviceStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
devEui: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
devEui: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -28,7 +22,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class DevAddrInput extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -49,7 +42,7 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value) {
|
||||
@ -61,7 +54,7 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
|
||||
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let v = e.target.value;
|
||||
const match = v.match(/[A-Fa-f0-9]/g);
|
||||
const match = v.match(/[A-Fa-f0-9]/g);
|
||||
|
||||
let value = "";
|
||||
if (match) {
|
||||
@ -72,10 +65,13 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: value,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
onByteOrderSelect = (v: string) => {
|
||||
if (v === this.state.byteOrder) {
|
||||
@ -89,21 +85,27 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
const current = this.state.value;
|
||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||
|
||||
this.setState({
|
||||
value: bytes.reverse().join(""),
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: bytes.reverse().join(""),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
generateRandom = () => {
|
||||
let req = new GetRandomDevAddrRequest();
|
||||
req.setDevEui(this.props.devEui);
|
||||
|
||||
DeviceStore.getRandomDevAddr(req, (resp: GetRandomDevAddrResponse) => {
|
||||
this.setState({
|
||||
value: resp.getDevAddr(),
|
||||
}, this.updateField);
|
||||
this.setState(
|
||||
{
|
||||
value: resp.getDevAddr(),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const addon = (
|
||||
@ -112,22 +114,33 @@ class DevAddrInput extends Component<IProps, IState> {
|
||||
<Select.Option value="msb">MSB</Select.Option>
|
||||
<Select.Option value="lsb">LSB</Select.Option>
|
||||
</Select>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
||||
<ReloadOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
>
|
||||
<Input hidden />
|
||||
<Input id={`${this.props.name}Render`} onChange={this.onChange} addonAfter={!this.props.disabled && addon} style={{fontFamily: "monospace"}} value={this.state.value} disabled={this.props.disabled} />
|
||||
<Input
|
||||
id={`${this.props.name}Render`}
|
||||
onChange={this.onChange}
|
||||
addonAfter={!this.props.disabled && addon}
|
||||
style={{ fontFamily: "monospace" }}
|
||||
value={this.state.value}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ import { Input, Select, Button, Space, Form } from "antd";
|
||||
import { ReloadOutlined } from "@ant-design/icons";
|
||||
|
||||
interface IProps {
|
||||
formRef: React.RefObject<any>,
|
||||
label: string,
|
||||
name: string,
|
||||
formRef: React.RefObject<any>;
|
||||
label: string;
|
||||
name: string;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
@ -17,7 +17,6 @@ interface IState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
||||
class EuiInput extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -38,7 +37,7 @@ class EuiInput extends Component<IProps, IState> {
|
||||
this.props.formRef.current.setFieldsValue({
|
||||
[this.props.name]: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.value) {
|
||||
@ -50,7 +49,7 @@ class EuiInput extends Component<IProps, IState> {
|
||||
|
||||
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let v = e.target.value;
|
||||
const match = v.match(/[A-Fa-f0-9]/g);
|
||||
const match = v.match(/[A-Fa-f0-9]/g);
|
||||
|
||||
let value = "";
|
||||
if (match) {
|
||||
@ -61,10 +60,13 @@ class EuiInput extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: value,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
onByteOrderSelect = (v: string) => {
|
||||
if (v === this.state.byteOrder) {
|
||||
@ -78,21 +80,27 @@ class EuiInput extends Component<IProps, IState> {
|
||||
const current = this.state.value;
|
||||
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
||||
|
||||
this.setState({
|
||||
value: bytes.reverse().join(""),
|
||||
}, this.updateField);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
value: bytes.reverse().join(""),
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
generateRandom = () => {
|
||||
let cryptoObj = window.crypto || window.Crypto;
|
||||
let b = new Uint8Array(8);
|
||||
cryptoObj.getRandomValues(b);
|
||||
|
||||
let key = Buffer.from(b).toString('hex');
|
||||
this.setState({
|
||||
value: key,
|
||||
}, this.updateField);
|
||||
}
|
||||
let key = Buffer.from(b).toString("hex");
|
||||
this.setState(
|
||||
{
|
||||
value: key,
|
||||
},
|
||||
this.updateField,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const addon = (
|
||||
@ -101,22 +109,33 @@ class EuiInput extends Component<IProps, IState> {
|
||||
<Select.Option value="msb">MSB</Select.Option>
|
||||
<Select.Option value="lsb">LSB</Select.Option>
|
||||
</Select>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}><ReloadOutlined /></Button>
|
||||
<Button type="text" size="small" shape="circle" onClick={this.generateRandom}>
|
||||
<ReloadOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<Form.Item
|
||||
rules={[{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{16}/g),
|
||||
}]}
|
||||
rules={[
|
||||
{
|
||||
required: this.props.required,
|
||||
message: `Please enter a valid ${this.props.label}`,
|
||||
pattern: new RegExp(/[A-Fa-f0-9]{16}/g),
|
||||
},
|
||||
]}
|
||||
label={this.props.label}
|
||||
name={this.props.name}
|
||||
>
|
||||
<Input hidden />
|
||||
<Input id={`${this.props.name}Render`} onChange={this.onChange} addonAfter={!this.props.disabled && addon} style={{fontFamily: "monospace"}} value={this.state.value} disabled={this.props.disabled} />
|
||||
<Input
|
||||
id={`${this.props.name}Render`}
|
||||
onChange={this.onChange}
|
||||
addonAfter={!this.props.disabled && addon}
|
||||
style={{ fontFamily: "monospace" }}
|
||||
value={this.state.value}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
@ -13,27 +13,20 @@ import {
|
||||
|
||||
import InternalStore from "../stores/InternalStore";
|
||||
|
||||
|
||||
interface IProps {
|
||||
user: User;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
searchResult?: GlobalSearchResponse;
|
||||
settings?: SettingsResponse,
|
||||
settings?: SettingsResponse;
|
||||
}
|
||||
|
||||
const renderTitle = (title: string) => (
|
||||
<span>
|
||||
{title}
|
||||
</span>
|
||||
);
|
||||
const renderTitle = (title: string) => <span>{title}</span>;
|
||||
|
||||
const renderItem = (title: string, url: string) => ({
|
||||
value: title,
|
||||
label: (
|
||||
<Link to={url}>{title}</Link>
|
||||
),
|
||||
label: <Link to={url}>{title}</Link>,
|
||||
});
|
||||
|
||||
class Header extends Component<IProps, IState> {
|
||||
@ -65,7 +58,7 @@ class Header extends Component<IProps, IState> {
|
||||
searchResult: resp,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.settings === undefined) {
|
||||
@ -76,9 +69,11 @@ class Header extends Component<IProps, IState> {
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
{!oidcEnabled && <Menu.Item>
|
||||
<Link to={`/users/${this.props.user.getId()}/password`}>Change password</Link>
|
||||
</Menu.Item>}
|
||||
{!oidcEnabled && (
|
||||
<Menu.Item>
|
||||
<Link to={`/users/${this.props.user.getId()}/password`}>Change password</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item>
|
||||
<Link to="/login">Logout</Link>
|
||||
</Menu.Item>
|
||||
@ -90,19 +85,19 @@ class Header extends Component<IProps, IState> {
|
||||
options: any[];
|
||||
}[] = [
|
||||
{
|
||||
label: renderTitle('Tenants'),
|
||||
label: renderTitle("Tenants"),
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
label: renderTitle('Gateways'),
|
||||
label: renderTitle("Gateways"),
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
label: renderTitle('Applications'),
|
||||
label: renderTitle("Applications"),
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
label: renderTitle('Devices'),
|
||||
label: renderTitle("Devices"),
|
||||
options: [],
|
||||
},
|
||||
];
|
||||
@ -110,24 +105,36 @@ class Header extends Component<IProps, IState> {
|
||||
if (this.state.searchResult !== undefined) {
|
||||
for (const res of this.state.searchResult.getResultList()) {
|
||||
if (res.getKind() === "tenant") {
|
||||
options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`))
|
||||
options[0].options.push(renderItem(res.getTenantName(), `/tenants/${res.getTenantId()}`));
|
||||
}
|
||||
|
||||
if (res.getKind() === "gateway") {
|
||||
options[1].options.push(renderItem(res.getGatewayName(), `/tenants/${res.getTenantId()}/gateways/${res.getGatewayId()}`))
|
||||
options[1].options.push(
|
||||
renderItem(res.getGatewayName(), `/tenants/${res.getTenantId()}/gateways/${res.getGatewayId()}`),
|
||||
);
|
||||
}
|
||||
|
||||
if (res.getKind() === "application") {
|
||||
options[2].options.push(renderItem(res.getApplicationName(), `/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}`))
|
||||
options[2].options.push(
|
||||
renderItem(
|
||||
res.getApplicationName(),
|
||||
`/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (res.getKind() === "device") {
|
||||
options[3].options.push(renderItem(res.getDeviceName(), `/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}/devices/${res.getDeviceDevEui()}`));
|
||||
options[3].options.push(
|
||||
renderItem(
|
||||
res.getDeviceName(),
|
||||
`/tenants/${res.getTenantId()}/applications/${res.getApplicationId()}/devices/${res.getDeviceDevEui()}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<img className="logo" alt="ChirpStack" src="/logo.png" />
|
||||
<div className="actions">
|
||||
@ -138,15 +145,19 @@ class Header extends Component<IProps, IState> {
|
||||
options={options}
|
||||
onSearch={this.onSearch}
|
||||
>
|
||||
<Input.Search placeholder="Search..." style={{width: 500, marginTop: -5}} />
|
||||
<Input.Search placeholder="Search..." style={{ width: 500, marginTop: -5 }} />
|
||||
</AutoComplete>
|
||||
</div>
|
||||
<div className="help">
|
||||
<a href="https://www.chirpstack.io" target="_blank" rel="noreferrer"><Button icon={<QuestionOutlined />}/></a>
|
||||
<a href="https://www.chirpstack.io" target="_blank" rel="noreferrer">
|
||||
<Button icon={<QuestionOutlined />} />
|
||||
</a>
|
||||
</div>
|
||||
<div className="user">
|
||||
<Dropdown overlay={menu} placement="bottomRight" trigger={['click']}>
|
||||
<Button type="primary" icon={<UserOutlined />}>{this.props.user.getEmail()} <DownOutlined /></Button>
|
||||
<Dropdown overlay={menu} placement="bottomRight" trigger={["click"]}>
|
||||
<Button type="primary" icon={<UserOutlined />}>
|
||||
{this.props.user.getEmail()} <DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,28 +3,25 @@ import React, { Component } from "react";
|
||||
import { color } from "chart.js/helpers";
|
||||
import { Chart } from "react-chartjs-2";
|
||||
|
||||
|
||||
interface HeatmapData {
|
||||
x: string;
|
||||
y: Array<[string, number]>;
|
||||
}
|
||||
|
||||
|
||||
interface IProps {
|
||||
data: HeatmapData[];
|
||||
fromColor: string;
|
||||
toColor: string;
|
||||
}
|
||||
|
||||
|
||||
class Heatmap extends Component<IProps> {
|
||||
render() {
|
||||
if (this.props.data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let xSet: {[key: string]: any} = {};
|
||||
let ySet: {[key: string]: any} = {};
|
||||
let xSet: { [key: string]: any } = {};
|
||||
let ySet: { [key: string]: any } = {};
|
||||
|
||||
let dataData: {
|
||||
x: string;
|
||||
@ -45,18 +42,22 @@ class Heatmap extends Component<IProps> {
|
||||
fromColor: this.props.fromColor.match(/\d+/g)!.map(Number),
|
||||
toColor: this.props.toColor.match(/\d+/g)!.map(Number),
|
||||
backgroundColor: (ctx: any): string => {
|
||||
if (ctx.dataset === undefined || ctx.dataset.data === undefined || ctx.dataset.data[ctx.dataIndex] === undefined) {
|
||||
return color('white').rgbString();
|
||||
if (
|
||||
ctx.dataset === undefined ||
|
||||
ctx.dataset.data === undefined ||
|
||||
ctx.dataset.data[ctx.dataIndex] === undefined
|
||||
) {
|
||||
return color("white").rgbString();
|
||||
}
|
||||
|
||||
const value = ctx.dataset.data[ctx.dataIndex].v;
|
||||
const steps = ctx.dataset.maxValue - ctx.dataset.minValue + 1;
|
||||
const step = value - ctx.dataset.minValue;
|
||||
const factor = 1 / steps * step;
|
||||
const factor = (1 / steps) * step;
|
||||
|
||||
let result: [number, number, number] = ctx.dataset.fromColor.slice();
|
||||
for (var i = 0; i < 3; i++) {
|
||||
result[i] = Math.round(result[i] + factor * (ctx.dataset.toColor[i] - ctx.dataset.fromColor[i]));
|
||||
result[i] = Math.round(result[i] + factor * (ctx.dataset.toColor[i] - ctx.dataset.fromColor[i]));
|
||||
}
|
||||
|
||||
return color(result).rgbString();
|
||||
@ -94,19 +95,18 @@ class Heatmap extends Component<IProps> {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: {display: false},
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
title: () => {
|
||||
return '';
|
||||
return "";
|
||||
},
|
||||
label: (ctx: any) => {
|
||||
const v = ctx.dataset.data[ctx.dataIndex].v;
|
||||
return 'Count: ' + v;
|
||||
return "Count: " + v;
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -136,9 +136,7 @@ class Heatmap extends Component<IProps> {
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<Chart type="matrix" data={data} options={options} />
|
||||
);
|
||||
return <Chart type="matrix" data={data} options={options} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,10 @@ import moment from "moment";
|
||||
import JSONTreeOriginal from "react-json-tree";
|
||||
|
||||
import { Tag, Drawer, Button, Table, Spin } from "antd";
|
||||
import { ZoomInOutlined } from '@ant-design/icons';
|
||||
import { ZoomInOutlined } from "@ant-design/icons";
|
||||
|
||||
import { LogItem } from "@chirpstack/chirpstack-api-grpc-web/api/internal_pb";
|
||||
|
||||
|
||||
interface IProps {
|
||||
logs: LogItem[];
|
||||
}
|
||||
@ -15,8 +14,7 @@ interface IProps {
|
||||
interface IState {
|
||||
drawerOpen: boolean;
|
||||
body: any;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class LogTable extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
@ -32,7 +30,7 @@ class LogTable extends Component<IProps, IState> {
|
||||
this.setState({
|
||||
drawerOpen: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onDrawerOpen = (body: any) => {
|
||||
return () => {
|
||||
@ -41,47 +39,59 @@ class LogTable extends Component<IProps, IState> {
|
||||
drawerOpen: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let items = this.props.logs.map((l, i) => l.toObject());
|
||||
let body = JSON.parse(this.state.body);
|
||||
|
||||
const theme = {
|
||||
scheme: 'google',
|
||||
author: 'seth wright (http://sethawright.com)',
|
||||
base00: '#000000',
|
||||
base01: '#282a2e',
|
||||
base02: '#373b41',
|
||||
base03: '#969896',
|
||||
base04: '#b4b7b4',
|
||||
base05: '#c5c8c6',
|
||||
base06: '#e0e0e0',
|
||||
base07: '#ffffff',
|
||||
base08: '#CC342B',
|
||||
base09: '#F96A38',
|
||||
base0A: '#FBA922',
|
||||
base0B: '#198844',
|
||||
base0C: '#3971ED',
|
||||
base0D: '#3971ED',
|
||||
base0E: '#A36AC7',
|
||||
base0F: '#3971ED',
|
||||
scheme: "google",
|
||||
author: "seth wright (http://sethawright.com)",
|
||||
base00: "#000000",
|
||||
base01: "#282a2e",
|
||||
base02: "#373b41",
|
||||
base03: "#969896",
|
||||
base04: "#b4b7b4",
|
||||
base05: "#c5c8c6",
|
||||
base06: "#e0e0e0",
|
||||
base07: "#ffffff",
|
||||
base08: "#CC342B",
|
||||
base09: "#F96A38",
|
||||
base0A: "#FBA922",
|
||||
base0B: "#198844",
|
||||
base0C: "#3971ED",
|
||||
base0D: "#3971ED",
|
||||
base0E: "#A36AC7",
|
||||
base0F: "#3971ED",
|
||||
};
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<Drawer title="Details" placement="right" width={650} onClose={this.onDrawerClose} visible={this.state.drawerOpen}>
|
||||
<Drawer
|
||||
title="Details"
|
||||
placement="right"
|
||||
width={650}
|
||||
onClose={this.onDrawerClose}
|
||||
visible={this.state.drawerOpen}
|
||||
>
|
||||
<JSONTreeOriginal
|
||||
data={body}
|
||||
theme={theme}
|
||||
hideRoot={true}
|
||||
shouldExpandNode={() => {return true}}
|
||||
shouldExpandNode={() => {
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
{items.length !== 0 && <div className="spinner"><Spin /></div>}
|
||||
{items.length !== 0 && (
|
||||
<div className="spinner">
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
<Table
|
||||
showHeader={false}
|
||||
loading={items.length === 0}
|
||||
loading={items.length === 0}
|
||||
dataSource={items}
|
||||
pagination={false}
|
||||
columns={[
|
||||
@ -94,26 +104,43 @@ class LogTable extends Component<IProps, IState> {
|
||||
let ts = new Date(0);
|
||||
ts.setUTCSeconds(obj.time!.seconds);
|
||||
return moment(ts).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Type",
|
||||
dataIndex: "description",
|
||||
key: "description",
|
||||
width: 200,
|
||||
render: (text, obj) => <Button icon={<ZoomInOutlined />} type="primary" shape="round" size="small" onClick={this.onDrawerOpen(obj.body)}>{text}</Button>,
|
||||
render: (text, obj) => (
|
||||
<Button
|
||||
icon={<ZoomInOutlined />}
|
||||
type="primary"
|
||||
shape="round"
|
||||
size="small"
|
||||
onClick={this.onDrawerOpen(obj.body)}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Properties",
|
||||
dataIndex: "properties",
|
||||
key: "properties",
|
||||
render: (text, obj) => obj.propertiesMap.map((p, i) => {
|
||||
if (p[1] !== "") {
|
||||
return <Tag><pre>{p[0]}: {p[1]}</pre></Tag>
|
||||
}
|
||||
render: (text, obj) =>
|
||||
obj.propertiesMap.map((p, i) => {
|
||||
if (p[1] !== "") {
|
||||
return (
|
||||
<Tag>
|
||||
<pre>
|
||||
{p[0]}: {p[1]}
|
||||
</pre>
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
return null;
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
@ -3,8 +3,7 @@ import React, { Component } from "react";
|
||||
import L, { LatLngTuple, FitBoundsOptions } from "leaflet";
|
||||
import "leaflet.awesome-markers";
|
||||
import { MarkerProps as LMarkerProps } from "react-leaflet";
|
||||
import { MapContainer, Marker as LMarker, TileLayer } from 'react-leaflet';
|
||||
|
||||
import { MapContainer, Marker as LMarker, TileLayer } from "react-leaflet";
|
||||
|
||||
interface IProps {
|
||||
height: number;
|
||||
@ -17,7 +16,6 @@ interface IState {
|
||||
map?: L.Map;
|
||||
}
|
||||
|
||||
|
||||
class Map extends Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@ -25,22 +23,25 @@ class Map extends Component<IProps, IState> {
|
||||
}
|
||||
|
||||
setMap = (map: L.Map) => {
|
||||
this.setState({
|
||||
map: map,
|
||||
}, () => {
|
||||
// This is needed as setMap is called after the map has been created.
|
||||
// There is a small amount of time where componentDidUpdate can't update
|
||||
// the map with the new center because setMap hasn't been called yet.
|
||||
// In such case, the map would never update to the new center.
|
||||
if (this.props.center !== undefined) {
|
||||
map.panTo(this.props.center);
|
||||
}
|
||||
this.setState(
|
||||
{
|
||||
map: map,
|
||||
},
|
||||
() => {
|
||||
// This is needed as setMap is called after the map has been created.
|
||||
// There is a small amount of time where componentDidUpdate can't update
|
||||
// the map with the new center because setMap hasn't been called yet.
|
||||
// In such case, the map would never update to the new center.
|
||||
if (this.props.center !== undefined) {
|
||||
map.panTo(this.props.center);
|
||||
}
|
||||
|
||||
if (this.props.bounds !== undefined) {
|
||||
map.fitBounds(this.props.bounds, this.props.boundsOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.props.bounds !== undefined) {
|
||||
map.fitBounds(this.props.bounds, this.props.boundsOptions);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
componentDidUpdate(oldProps: IProps) {
|
||||
if (this.props === oldProps) {
|
||||
@ -63,7 +64,7 @@ class Map extends Component<IProps, IState> {
|
||||
height: this.props.height,
|
||||
};
|
||||
|
||||
return(
|
||||
return (
|
||||
<MapContainer
|
||||
bounds={this.props.bounds}
|
||||
boundsOptions={this.props.boundsOptions}
|
||||
@ -84,7 +85,17 @@ class Map extends Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
export type MarkerColor = "red" | "darkred" | "orange" | "green" | "darkgreen" | "blue" | "purple" | "darkpurple" | "cadetblue" | undefined;
|
||||
export type MarkerColor =
|
||||
| "red"
|
||||
| "darkred"
|
||||
| "orange"
|
||||
| "green"
|
||||
| "darkgreen"
|
||||
| "blue"
|
||||
| "purple"
|
||||
| "darkpurple"
|
||||
| "cadetblue"
|
||||
| undefined;
|
||||
|
||||
interface MarkerProps extends LMarkerProps {
|
||||
position: [number, number];
|
||||
@ -100,11 +111,12 @@ export class Marker extends Component<MarkerProps> {
|
||||
icon: faIcon,
|
||||
prefix: "fa",
|
||||
markerColor: color,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return(
|
||||
<LMarker icon={iconMarker} position={position} {...otherProps}>{this.props.children}</LMarker>
|
||||
return (
|
||||
<LMarker icon={iconMarker} position={position} {...otherProps}>
|
||||
{this.props.children}
|
||||
</LMarker>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,22 @@ import React, { Component } from "react";
|
||||
import { withRouter, RouteComponentProps, Link } from "react-router-dom";
|
||||
|
||||
import { Menu, MenuProps } from "antd";
|
||||
import { CloudOutlined, HomeOutlined, UserOutlined, DashboardOutlined, KeyOutlined, WifiOutlined, ControlOutlined, AppstoreOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
CloudOutlined,
|
||||
HomeOutlined,
|
||||
UserOutlined,
|
||||
DashboardOutlined,
|
||||
KeyOutlined,
|
||||
WifiOutlined,
|
||||
ControlOutlined,
|
||||
AppstoreOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
import { GetTenantResponse, ListTenantsRequest, ListTenantsResponse } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
import {
|
||||
GetTenantResponse,
|
||||
ListTenantsRequest,
|
||||
ListTenantsResponse,
|
||||
} from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||
|
||||
import Autocomplete, { OptionCallbackFunc, OptionsCallbackFunc } from "../components/Autocomplete";
|
||||
import TenantStore from "../stores/TenantStore";
|
||||
@ -48,7 +61,7 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
||||
this.setState({
|
||||
tenantId: SessionStore.getTenantId(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getTenantOptions = (search: string, fn: OptionsCallbackFunc) => {
|
||||
let req = new ListTenantsRequest();
|
||||
@ -56,85 +69,86 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
||||
req.setLimit(10);
|
||||
|
||||
TenantStore.list(req, (resp: ListTenantsResponse) => {
|
||||
const options = resp.getResultList().map((o, i) => {return {label: o.getName(), value: o.getId()}});
|
||||
const options = resp.getResultList().map((o, i) => {
|
||||
return { label: o.getName(), value: o.getId() };
|
||||
});
|
||||
fn(options);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getTenantOption = (id: string, fn: OptionCallbackFunc) => {
|
||||
TenantStore.get(id, (resp: GetTenantResponse) => {
|
||||
const tenant = resp.getTenant();
|
||||
if (tenant) {
|
||||
fn({label: tenant.getName(), value: tenant.getId()});
|
||||
fn({ label: tenant.getName(), value: tenant.getId() });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onTenantSelect = (value: string) => {
|
||||
SessionStore.setTenantId(value);
|
||||
this.props.history.push(`/tenants/${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
parseLocation = () => {
|
||||
const path = this.props.history.location.pathname;
|
||||
const tenantRe = /\/tenants\/([\w-]{36})/g;
|
||||
const match = tenantRe.exec(path);
|
||||
|
||||
if (match !== null && (this.state.tenantId !== match[1])) {
|
||||
if (match !== null && this.state.tenantId !== match[1]) {
|
||||
SessionStore.setTenantId(match[1]);
|
||||
}
|
||||
|
||||
// ns dashboard
|
||||
if (path === "/dashboard") {
|
||||
this.setState({selectedKey: "ns-dashboard"});
|
||||
this.setState({ selectedKey: "ns-dashboard" });
|
||||
}
|
||||
|
||||
// ns tenants
|
||||
if (/\/tenants(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||
this.setState({selectedKey: "ns-tenants"});
|
||||
this.setState({ selectedKey: "ns-tenants" });
|
||||
}
|
||||
|
||||
// ns tenants
|
||||
if (/\/users(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||
this.setState({selectedKey: "ns-users"});
|
||||
this.setState({ selectedKey: "ns-users" });
|
||||
}
|
||||
|
||||
// ns api keys
|
||||
if (/\/api-keys(\/([\w-]{36}\/edit|create))?/g.exec(path)) {
|
||||
this.setState({selectedKey: "ns-api-keys"});
|
||||
this.setState({ selectedKey: "ns-api-keys" });
|
||||
}
|
||||
|
||||
|
||||
// tenant dashboard
|
||||
if (/\/tenants\/[\w-]{36}/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-dashboard"});
|
||||
this.setState({ selectedKey: "tenant-dashboard" });
|
||||
}
|
||||
|
||||
// tenant users
|
||||
if (/\/tenants\/[\w-]{36}\/users.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-users"});
|
||||
this.setState({ selectedKey: "tenant-users" });
|
||||
}
|
||||
|
||||
// tenant api-keys
|
||||
if (/\/tenants\/[\w-]{36}\/api-keys.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-api-keys"});
|
||||
this.setState({ selectedKey: "tenant-api-keys" });
|
||||
}
|
||||
|
||||
// tenant device-profiles
|
||||
if (/\/tenants\/[\w-]{36}\/device-profiles.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-device-profiles"});
|
||||
this.setState({ selectedKey: "tenant-device-profiles" });
|
||||
}
|
||||
|
||||
// tenant gateways
|
||||
if (/\/tenants\/[\w-]{36}\/gateways.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-gateways"});
|
||||
this.setState({ selectedKey: "tenant-gateways" });
|
||||
}
|
||||
|
||||
// tenant applications
|
||||
if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) {
|
||||
this.setState({selectedKey: "tenant-applications"});
|
||||
this.setState({ selectedKey: "tenant-applications" });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const tenantId = this.state.tenantId;
|
||||
@ -170,13 +184,13 @@ class SideMenu extends Component<RouteComponentProps, IState> {
|
||||
});
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<Autocomplete
|
||||
placeholder="Select tenant"
|
||||
className="organiation-select"
|
||||
getOption={this.getTenantOption}
|
||||
getOptions={this.getTenantOptions}
|
||||
getOptions={this.getTenantOptions}
|
||||
onSelect={this.onTenantSelect}
|
||||
value={this.state.tenantId}
|
||||
/>
|
||||
|
Reference in New Issue
Block a user