mirror of https://github.com/apache/druid.git
Migrate the web console back to Blueprint v3 (#7398)
* migrate back to Blueprint v3 * rename filler file * fix missing icon
This commit is contained in:
parent
1137bb2586
commit
d2ff3eb199
|
@ -66,7 +66,6 @@ public class RouterJettyServerInitializer implements JettyServerInitializer
|
|||
"/",
|
||||
"/coordinator-console/*",
|
||||
"/public/*",
|
||||
"/assets/*",
|
||||
"/old-console/*",
|
||||
"/pages/*",
|
||||
"/unified-console.html",
|
||||
|
|
|
@ -2,7 +2,6 @@ node/
|
|||
node_modules/
|
||||
resources/
|
||||
public/
|
||||
assets/
|
||||
lib/*.css
|
||||
|
||||
coordinator-console/
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "web-console",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0",
|
||||
"description": "A web console for Apache Druid",
|
||||
"author": "Imply Data Inc.",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -20,7 +20,7 @@
|
|||
"stylelint": "stylelint 'src/**/*.scss'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blueprintjs/core": "1.0.1",
|
||||
"@blueprintjs/core": "^3.15.0",
|
||||
"axios": "^0.18.0",
|
||||
"brace": "^0.11.1",
|
||||
"classnames": "^2.2.6",
|
||||
|
@ -31,10 +31,8 @@
|
|||
"hjson": "^3.1.2",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"numeral": "^2.0.6",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.3",
|
||||
"react-ace": "^6.4.0",
|
||||
"react-addons-css-transition-group": "^15.6.2",
|
||||
"react-dom": "^16.8.3",
|
||||
"react-router": "^4.3.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
|
|
|
@ -23,10 +23,6 @@ cp -r ./node_modules/druid-console/coordinator-console .
|
|||
cp -r ./node_modules/druid-console/pages .
|
||||
cp ./node_modules/druid-console/index.html .
|
||||
|
||||
echo "Copying blueprint assets in..."
|
||||
sed 's|url("assets|url("/assets|g' "./node_modules/@blueprintjs/core/dist/blueprint.css" > lib/blueprint.css
|
||||
cp -r "./node_modules/@blueprintjs/core/dist/assets" .
|
||||
|
||||
echo "Adding SQL function doc..."
|
||||
PATH="./target/node:$PATH" ./script/create-sql-function-doc
|
||||
|
||||
|
|
|
@ -22,5 +22,4 @@ rm -rf \
|
|||
coordinator-console \
|
||||
pages \
|
||||
public \
|
||||
assets \
|
||||
index.html
|
||||
|
|
|
@ -24,4 +24,3 @@ cp -r coordinator-console "$1"
|
|||
cp -r old-console "$1"
|
||||
cp -r pages "$1"
|
||||
cp -r public "$1"
|
||||
cp -r assets "$1"
|
||||
|
|
|
@ -25,7 +25,7 @@ import { countBy, makeTextFilter } from '../utils';
|
|||
|
||||
class FullButton extends React.Component {
|
||||
render() {
|
||||
return <Button className="pt-fill" {...this.props}/>;
|
||||
return <Button fill {...this.props}/>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
*/
|
||||
|
||||
import { InputGroup } from "@blueprintjs/core";
|
||||
import { FormGroup, HTMLSelect, NumericInput, TagInput } from "@blueprintjs/core";
|
||||
import * as React from 'react';
|
||||
|
||||
import { FormGroup, HTMLSelect, JSONInput, NumericInput, TagInput } from "../components/filler";
|
||||
import { JSONInput } from './json-input';
|
||||
|
||||
import "./auto-form.scss";
|
||||
|
||||
|
|
|
@ -1,375 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Collapse } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import AceEditor from "react-ace";
|
||||
|
||||
import { parseStringToJSON, stringifyJSON, validJson } from "../utils";
|
||||
|
||||
import './filler.scss';
|
||||
|
||||
export const IconNames = {
|
||||
ERROR: "error" as "error",
|
||||
PLUS: "plus" as "plus",
|
||||
REFRESH: "refresh" as "refresh",
|
||||
APPLICATION: "application" as "application",
|
||||
GRAPH: "graph" as "graph",
|
||||
MAP: "map" as "map",
|
||||
TH: "th" as "th",
|
||||
USER: "user" as "user",
|
||||
GIT_BRANCH: "git-branch" as "git-branch",
|
||||
COG: "cog" as "cog",
|
||||
MULTI_SELECT: "multi-select" as "multi-select",
|
||||
STACKED_CHART: "stacked-chart" as "stacked-chart",
|
||||
GANTT_CHART: "gantt-chart" as "gantt-chart",
|
||||
DATABASE: "database" as "database",
|
||||
SETTINGS: "settings" as "settings",
|
||||
HELP: "help" as "help",
|
||||
SHARE: "share" as "share",
|
||||
CROSS: "cross" as "cross",
|
||||
ARROW_LEFT: "arrow-left" as "arrow-left",
|
||||
CARET_RIGHT: "caret-right" as "caret-right",
|
||||
TICK: "tick" as "tick",
|
||||
ARROW_RIGHT: "arrow-right" as "arrow-right",
|
||||
TRASH: "trash" as "trash",
|
||||
CARET_DOWN: "caret-down" as "caret-down",
|
||||
ARROW_UP: "arrow-up" as "arrow-up",
|
||||
ARROW_DOWN: "arrow-down" as "arrow-down",
|
||||
PROPERTIES: "properties" as "properties",
|
||||
BUILD: "build" as "build",
|
||||
WRENCH: "wrench" as "wrench"
|
||||
};
|
||||
export type IconNames = typeof IconNames[keyof typeof IconNames];
|
||||
|
||||
export class H5 extends React.Component<{}, {}> {
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return <h5>{children}</h5>;
|
||||
}
|
||||
}
|
||||
|
||||
export class Card extends React.Component<{ interactive?: boolean }, {}> {
|
||||
render() {
|
||||
const { interactive, children } = this.props;
|
||||
return <div className={classNames("pt-card", { 'pt-interactive': interactive })}>
|
||||
{children}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class Icon extends React.Component<{ icon: string, color?: string }, {}> {
|
||||
render() {
|
||||
const { color, icon } = this.props;
|
||||
return <span className={classNames('pt-icon-standard', 'pt-icon-' + icon)} style={{ color }}/>;
|
||||
}
|
||||
}
|
||||
|
||||
export class ControlGroup extends React.Component<{}, {}> {
|
||||
render() {
|
||||
return <div className="pt-control-group" {...this.props}/>;
|
||||
}
|
||||
}
|
||||
|
||||
export class ButtonGroup extends React.Component<{ vertical?: boolean, fixed?: boolean }, {}> {
|
||||
render() {
|
||||
const { vertical, fixed, children } = this.props;
|
||||
return <div className={classNames("pt-button-group", { 'pt-vertical': vertical, 'pt-fixed': fixed })}>
|
||||
{children}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class Label extends React.Component<{}, {}> {
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return <label className="pt-label">{children}</label>;
|
||||
}
|
||||
}
|
||||
|
||||
export class FormGroup extends React.Component<{ className?: string, label?: string }, {}> {
|
||||
render() {
|
||||
const { className, label, children } = this.props;
|
||||
return <div className={classNames("form-group", className)}>
|
||||
{label ? <Label>{label}</Label> : null}
|
||||
{children}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export const Alignment = {
|
||||
LEFT: "left" as "left",
|
||||
RIGHT: "right" as "right"
|
||||
};
|
||||
export type Alignment = typeof Alignment[keyof typeof Alignment];
|
||||
|
||||
export class Navbar extends React.Component<{ className?: string }, {}> {
|
||||
render() {
|
||||
const { className, children } = this.props;
|
||||
return <nav className={classNames("pt-navbar", className)}>
|
||||
{children}
|
||||
</nav>;
|
||||
}
|
||||
}
|
||||
|
||||
export class NavbarGroup extends React.Component<{ align: Alignment }, {}> {
|
||||
render() {
|
||||
const { align, children } = this.props;
|
||||
return <div className={classNames('pt-navbar-group', 'pt-align-' + align)}>
|
||||
{children}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class NavbarDivider extends React.Component<{}, {}> {
|
||||
render() {
|
||||
return <span className="pt-navbar-divider"/>;
|
||||
}
|
||||
}
|
||||
|
||||
export class HTMLSelect extends React.Component<{ key?: string; style?: any; onChange: any; value: any; fill?: boolean }, {}> {
|
||||
render() {
|
||||
const { key, style, onChange, value, fill, children } = this.props;
|
||||
return <div className={classNames("pt-select", { 'pt-fill': fill })} key={key} style={style}>
|
||||
<select onChange={onChange} value={value}>{children}</select>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class TextArea extends React.Component<{ className?: string; onChange?: any; value?: string, readOnly?: boolean, style?: any}, {}> {
|
||||
render() {
|
||||
const { className, value, onChange, readOnly, style } = this.props;
|
||||
return <textarea
|
||||
readOnly={readOnly}
|
||||
className={classNames("pt-input", className)}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={style}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
export interface NumericInputProps {
|
||||
value: number | null;
|
||||
onValueChange: any;
|
||||
min?: number;
|
||||
max?: number;
|
||||
stepSize?: number;
|
||||
majorStepSize?: number;
|
||||
}
|
||||
|
||||
export class NumericInput extends React.Component<NumericInputProps, { stringValue: string }> {
|
||||
|
||||
static defaultProps = {
|
||||
stepSize: 1,
|
||||
majorStepSize: 10
|
||||
};
|
||||
|
||||
constructor(props: NumericInputProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
stringValue: typeof props.value === 'number' ? String(props.value) : ''
|
||||
};
|
||||
}
|
||||
|
||||
private constrain(n: number): number {
|
||||
const { min, max } = this.props;
|
||||
if (typeof min === 'number') n = Math.max(n, min);
|
||||
if (typeof max === 'number') n = Math.min(n, max);
|
||||
return n;
|
||||
}
|
||||
|
||||
private handleChange = (e: any) => {
|
||||
let stringValue = e.target.value.replace(/[^\d.+-]/g, '');
|
||||
let numValue = parseFloat(stringValue);
|
||||
if (isNaN(numValue)) {
|
||||
this.setState({ stringValue });
|
||||
} else {
|
||||
numValue = this.constrain(numValue);
|
||||
stringValue = String(numValue);
|
||||
this.setState({ stringValue });
|
||||
this.props.onValueChange(numValue);
|
||||
}
|
||||
}
|
||||
|
||||
private handleClick = (e: any, direction: number) => {
|
||||
const { stepSize, majorStepSize } = this.props;
|
||||
const { stringValue } = this.state;
|
||||
const diff = direction * (e.shiftKey ? majorStepSize as number : stepSize as number);
|
||||
const numValue = this.constrain((parseFloat(stringValue) || 0) + diff);
|
||||
this.setState({ stringValue: String(numValue) });
|
||||
this.props.onValueChange(numValue);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { stringValue } = this.state;
|
||||
|
||||
return <ControlGroup>
|
||||
<input className="pt-input" value={stringValue} onChange={this.handleChange}/>
|
||||
<ButtonGroup fixed>
|
||||
<Button iconName="caret-up" onClick={(e: any) => this.handleClick(e, +1)}/>
|
||||
<Button iconName="caret-down" onClick={(e: any) => this.handleClick(e, -1)}/>
|
||||
</ButtonGroup>
|
||||
</ControlGroup>;
|
||||
}
|
||||
}
|
||||
|
||||
export interface TagInputProps {
|
||||
values: string[];
|
||||
onChange: any;
|
||||
fill?: boolean;
|
||||
}
|
||||
|
||||
export class TagInput extends React.Component<TagInputProps, { stringValue: string }> {
|
||||
constructor(props: TagInputProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
stringValue: Array.isArray(props.values) ? props.values.join(', ') : ''
|
||||
};
|
||||
}
|
||||
|
||||
handleChange = (e: any) => {
|
||||
const stringValue = e.target.value;
|
||||
const newValues = stringValue.split(',').map((v: string) => v.trim());
|
||||
const newValuesFiltered = newValues.filter(Boolean);
|
||||
this.setState({
|
||||
stringValue: newValues.length === newValuesFiltered.length ? newValues.join(', ') : stringValue
|
||||
});
|
||||
this.props.onChange(newValuesFiltered);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { fill } = this.props;
|
||||
const { stringValue } = this.state;
|
||||
return <input
|
||||
className={classNames("pt-input", {'pt-fill': fill })}
|
||||
value={stringValue}
|
||||
onChange={this.handleChange}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
interface JSONInputProps extends React.Props<any> {
|
||||
onChange: (newJSONValue: any) => void;
|
||||
value: any;
|
||||
updateInputValidity?: (valueValid: boolean) => void;
|
||||
focus?: boolean;
|
||||
width?: string;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
interface JSONInputState {
|
||||
stringValue: string;
|
||||
}
|
||||
|
||||
export class JSONInput extends React.Component<JSONInputProps, JSONInputState> {
|
||||
constructor(props: JSONInputProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
stringValue: ""
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
const { value } = this.props;
|
||||
const stringValue = stringifyJSON(value);
|
||||
this.setState({
|
||||
stringValue
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: JSONInputProps): void {
|
||||
if (JSON.stringify(nextProps.value) !== JSON.stringify(this.props.value)) {
|
||||
this.setState({
|
||||
stringValue: stringifyJSON(nextProps.value)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onChange, updateInputValidity, focus, width, height } = this.props;
|
||||
const { stringValue } = this.state;
|
||||
return <AceEditor
|
||||
key={"hjson"}
|
||||
mode={"hjson"}
|
||||
theme="solarized_dark"
|
||||
name="ace-editor"
|
||||
onChange={(e: string) => {
|
||||
this.setState({stringValue: e});
|
||||
if (validJson(e) || e === "") onChange(parseStringToJSON(e));
|
||||
if (updateInputValidity) updateInputValidity(validJson(e) || e === '');
|
||||
}}
|
||||
focus={focus}
|
||||
fontSize={12}
|
||||
width={width || '100%'}
|
||||
height={height || "8vh"}
|
||||
showPrintMargin={false}
|
||||
showGutter={false}
|
||||
value={stringValue}
|
||||
editorProps={{
|
||||
$blockScrolling: Infinity
|
||||
}}
|
||||
setOptions={{
|
||||
enableBasicAutocompletion: false,
|
||||
enableLiveAutocompletion: false,
|
||||
showLineNumbers: false,
|
||||
tabSize: 2
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
interface JSONCollapseProps extends React.Props<any> {
|
||||
stringValue: string;
|
||||
buttonText: string;
|
||||
}
|
||||
|
||||
interface JSONCollapseState {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export class JSONCollapse extends React.Component<JSONCollapseProps, JSONCollapseState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { stringValue, buttonText} = this.props;
|
||||
const { isOpen } = this.state;
|
||||
const prettyValue = JSON.stringify(JSON.parse(stringValue), undefined, 2);
|
||||
return <div className={"json-collapse"}>
|
||||
<Button
|
||||
className={`pt-minimal ${isOpen ? " pt-active" : ""}`}
|
||||
onClick={() => this.setState({isOpen: !isOpen})}
|
||||
text={buttonText}
|
||||
/>
|
||||
<div>
|
||||
<Collapse isOpen={isOpen}>
|
||||
<TextArea
|
||||
readOnly
|
||||
value={prettyValue}
|
||||
/>
|
||||
</Collapse>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -33,12 +33,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.config-popover .pt-popover-content,
|
||||
.legacy-popover .pt-popover-content {
|
||||
.config-popover .bp3-popover-content,
|
||||
.legacy-popover .bp3-popover-content {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.help-popover .pt-popover-content {
|
||||
.help-popover .bp3-popover-content {
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AnchorButton, Button, Classes, Menu, MenuItem, Popover, Position } from "@blueprintjs/core";
|
||||
import { Alignment, AnchorButton, Button, Classes, Menu, MenuItem, Navbar, NavbarDivider, NavbarGroup, Popover, Position } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Alignment, IconNames, Navbar, NavbarDivider, NavbarGroup } from "../components/filler";
|
||||
import { AboutDialog } from "../dialogs/about-dialog";
|
||||
import { CoordinatorDynamicConfigDialog } from '../dialogs/coordinator-dynamic-config';
|
||||
import { OverlordDynamicConfigDialog } from "../dialogs/overlord-dynamic-config";
|
||||
|
@ -110,21 +110,21 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
|
|||
const { aboutDialogOpen, coordinatorDynamicConfigDialogOpen, overlordDynamicConfigDialogOpen } = this.state;
|
||||
|
||||
const legacyMenu = <Menu>
|
||||
<MenuItem iconName={IconNames.GRAPH} text="Legacy coordinator console" href={LEGACY_COORDINATOR_CONSOLE} target="_blank" />
|
||||
<MenuItem iconName={IconNames.MAP} text="Legacy overlord console" href={LEGACY_OVERLORD_CONSOLE} target="_blank" />
|
||||
<MenuItem icon={IconNames.GRAPH} text="Legacy coordinator console" href={LEGACY_COORDINATOR_CONSOLE} target="_blank" />
|
||||
<MenuItem icon={IconNames.MAP} text="Legacy overlord console" href={LEGACY_OVERLORD_CONSOLE} target="_blank" />
|
||||
</Menu>;
|
||||
|
||||
const helpMenu = <Menu>
|
||||
<MenuItem iconName={IconNames.GRAPH} text="About" onClick={() => this.setState({ aboutDialogOpen: true })} />
|
||||
<MenuItem iconName={IconNames.TH} text="Docs" href={DRUID_DOCS} target="_blank" />
|
||||
<MenuItem iconName={IconNames.USER} text="User group" href={DRUID_USER_GROUP} target="_blank" />
|
||||
<MenuItem iconName={IconNames.GIT_BRANCH} text="GitHub" href={DRUID_GITHUB} target="_blank" />
|
||||
<MenuItem icon={IconNames.GRAPH} text="About" onClick={() => this.setState({ aboutDialogOpen: true })} />
|
||||
<MenuItem icon={IconNames.TH} text="Docs" href={DRUID_DOCS} target="_blank" />
|
||||
<MenuItem icon={IconNames.USER} text="User group" href={DRUID_USER_GROUP} target="_blank" />
|
||||
<MenuItem icon={IconNames.GIT_BRANCH} text="GitHub" href={DRUID_GITHUB} target="_blank" />
|
||||
</Menu>;
|
||||
|
||||
const configMenu = <Menu>
|
||||
<MenuItem iconName={IconNames.COG} text="Coordinator dynamic config" onClick={() => this.setState({ coordinatorDynamicConfigDialogOpen: true })}/>
|
||||
<MenuItem iconName={IconNames.WRENCH} text="Overlord dynamic config" onClick={() => this.setState({ overlordDynamicConfigDialogOpen: true })}/>
|
||||
<MenuItem iconName={IconNames.PROPERTIES} className={classNames(Classes.MINIMAL, { 'pt-active': active === 'lookups' })} text="Lookups" href="#lookups"/>
|
||||
<MenuItem icon={IconNames.COG} text="Coordinator dynamic config" onClick={() => this.setState({ coordinatorDynamicConfigDialogOpen: true })}/>
|
||||
<MenuItem icon={IconNames.WRENCH} text="Overlord dynamic config" onClick={() => this.setState({ overlordDynamicConfigDialogOpen: true })}/>
|
||||
<MenuItem icon={IconNames.PROPERTIES} active={active === 'lookups'} text="Lookups" href="#lookups"/>
|
||||
</Menu>;
|
||||
|
||||
return <Navbar className="header-bar">
|
||||
|
@ -133,22 +133,22 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
|
|||
{this.renderLogo()}
|
||||
</a>
|
||||
<NavbarDivider />
|
||||
<AnchorButton className={classNames(Classes.MINIMAL, { 'pt-active': active === 'datasources' })} iconName={IconNames.MULTI_SELECT} text="Datasources" href="#datasources" />
|
||||
<AnchorButton className={classNames(Classes.MINIMAL, { 'pt-active': active === 'segments' })} iconName={IconNames.STACKED_CHART} text="Segments" href="#segments" />
|
||||
<AnchorButton className={classNames(Classes.MINIMAL, { 'pt-active': active === 'tasks' })} iconName={IconNames.GANTT_CHART} text="Tasks" href="#tasks" />
|
||||
<AnchorButton className={classNames(Classes.MINIMAL, { 'pt-active': active === 'servers' })} iconName={IconNames.DATABASE} text="Data servers" href="#servers" />
|
||||
<AnchorButton minimal active={active === 'datasources'} icon={IconNames.MULTI_SELECT} text="Datasources" href="#datasources" />
|
||||
<AnchorButton minimal active={active === 'segments'} icon={IconNames.STACKED_CHART} text="Segments" href="#segments" />
|
||||
<AnchorButton minimal active={active === 'tasks'} icon={IconNames.GANTT_CHART} text="Tasks" href="#tasks" />
|
||||
<AnchorButton minimal active={active === 'servers'} icon={IconNames.DATABASE} text="Data servers" href="#servers" />
|
||||
<NavbarDivider />
|
||||
<AnchorButton className={classNames(Classes.MINIMAL, { 'pt-active': active === 'sql' })} iconName={IconNames.APPLICATION} text="SQL" href="#sql" />
|
||||
<AnchorButton minimal active={active === 'sql'} icon={IconNames.APPLICATION} text="SQL" href="#sql" />
|
||||
<Popover className="config-popover" content={configMenu} position={Position.BOTTOM_LEFT}>
|
||||
<Button className={Classes.MINIMAL} iconName={IconNames.SETTINGS} text="Config"/>
|
||||
<Button className={Classes.MINIMAL} icon={IconNames.SETTINGS} text="Config"/>
|
||||
</Popover>
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<Popover className="legacy-popover" content={legacyMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Button className={Classes.MINIMAL} iconName={IconNames.SHARE} text="Legacy" />
|
||||
<Button className={Classes.MINIMAL} icon={IconNames.SHARE} text="Legacy" />
|
||||
</Popover>
|
||||
<Popover className="help-popover" content={helpMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Button className={Classes.MINIMAL} iconName={IconNames.HELP} text="Help" />
|
||||
<Button className={Classes.MINIMAL} icon={IconNames.HELP} text="Help" />
|
||||
</Popover>
|
||||
</NavbarGroup>
|
||||
{ aboutDialogOpen ? <AboutDialog
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Collapse, TextArea } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
interface JSONCollapseProps extends React.Props<any> {
|
||||
stringValue: string;
|
||||
buttonText: string;
|
||||
}
|
||||
|
||||
interface JSONCollapseState {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export class JSONCollapse extends React.Component<JSONCollapseProps, JSONCollapseState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { stringValue, buttonText} = this.props;
|
||||
const { isOpen } = this.state;
|
||||
const prettyValue = JSON.stringify(JSON.parse(stringValue), undefined, 2);
|
||||
return <div className={"json-collapse"}>
|
||||
<Button
|
||||
minimal
|
||||
active={isOpen}
|
||||
onClick={() => this.setState({isOpen: !isOpen})}
|
||||
text={buttonText}
|
||||
/>
|
||||
<div>
|
||||
<Collapse isOpen={isOpen}>
|
||||
<TextArea
|
||||
readOnly
|
||||
value={prettyValue}
|
||||
/>
|
||||
</Collapse>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import AceEditor from "react-ace";
|
||||
|
||||
import { parseStringToJSON, stringifyJSON, validJson } from "../utils";
|
||||
|
||||
interface JSONInputProps extends React.Props<any> {
|
||||
onChange: (newJSONValue: any) => void;
|
||||
value: any;
|
||||
updateInputValidity?: (valueValid: boolean) => void;
|
||||
focus?: boolean;
|
||||
width?: string;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
interface JSONInputState {
|
||||
stringValue: string;
|
||||
}
|
||||
|
||||
export class JSONInput extends React.Component<JSONInputProps, JSONInputState> {
|
||||
constructor(props: JSONInputProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
stringValue: ""
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
const { value } = this.props;
|
||||
const stringValue = stringifyJSON(value);
|
||||
this.setState({
|
||||
stringValue
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: JSONInputProps): void {
|
||||
if (JSON.stringify(nextProps.value) !== JSON.stringify(this.props.value)) {
|
||||
this.setState({
|
||||
stringValue: stringifyJSON(nextProps.value)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onChange, updateInputValidity, focus, width, height } = this.props;
|
||||
const { stringValue } = this.state;
|
||||
return <AceEditor
|
||||
key={"hjson"}
|
||||
mode={"hjson"}
|
||||
theme="solarized_dark"
|
||||
name="ace-editor"
|
||||
onChange={(e: string) => {
|
||||
this.setState({stringValue: e});
|
||||
if (validJson(e) || e === "") onChange(parseStringToJSON(e));
|
||||
if (updateInputValidity) updateInputValidity(validJson(e) || e === '');
|
||||
}}
|
||||
focus={focus}
|
||||
fontSize={12}
|
||||
width={width || '100%'}
|
||||
height={height || "8vh"}
|
||||
showPrintMargin={false}
|
||||
showGutter={false}
|
||||
value={stringValue}
|
||||
editorProps={{
|
||||
$blockScrolling: Infinity
|
||||
}}
|
||||
setOptions={{
|
||||
enableBasicAutocompletion: false,
|
||||
enableLiveAutocompletion: false,
|
||||
showLineNumbers: false,
|
||||
tabSize: 2
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
}
|
|
@ -16,12 +16,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Collapse, InputGroup } from "@blueprintjs/core";
|
||||
import { Button, Card, Collapse, ControlGroup, FormGroup, HTMLSelect, InputGroup, NumericInput, TagInput } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from 'axios';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Card, ControlGroup, FormGroup, HTMLSelect, IconNames, NumericInput, TagInput } from "../components/filler";
|
||||
|
||||
import './rule-editor.scss';
|
||||
|
||||
export interface Rule {
|
||||
|
@ -150,7 +149,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
|
|||
const ruleTiers = Object.keys(tieredReplicants).sort();
|
||||
return ruleTiers.map(tier => {
|
||||
return <ControlGroup key={tier}>
|
||||
<Button className="pt-minimal" style={{pointerEvents: 'none'}}>Replicants:</Button>
|
||||
<Button minimal style={{pointerEvents: 'none'}}>Replicants:</Button>
|
||||
<NumericInput
|
||||
value={tieredReplicants[tier]}
|
||||
onValueChange={(v: number) => {
|
||||
|
@ -160,7 +159,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
|
|||
min={1}
|
||||
max={256}
|
||||
/>
|
||||
<Button className="pt-minimal" style={{pointerEvents: 'none'}}>Tier:</Button>
|
||||
<Button minimal style={{pointerEvents: 'none'}}>Tier:</Button>
|
||||
<HTMLSelect
|
||||
fill
|
||||
value={tier}
|
||||
|
@ -175,7 +174,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
|
|||
<Button
|
||||
disabled={ruleTiers.length === 1}
|
||||
onClick={() => this.removeTier(tier)}
|
||||
iconName={IconNames.TRASH}
|
||||
icon={IconNames.TRASH}
|
||||
/>
|
||||
</ControlGroup>;
|
||||
});
|
||||
|
@ -186,7 +185,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
|
|||
if (Object.keys(rule.tieredReplicants || {}).length >= Object.keys(tiers).length) return null;
|
||||
|
||||
return <FormGroup className="right">
|
||||
<Button onClick={this.addTier} className="pt-minimal" iconName={IconNames.PLUS}>Add a tier</Button>
|
||||
<Button onClick={this.addTier} minimal icon={IconNames.PLUS}>Add a tier</Button>
|
||||
</FormGroup>;
|
||||
}
|
||||
|
||||
|
@ -213,13 +212,13 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
|
|||
|
||||
return <div className="rule-editor">
|
||||
<div className="title">
|
||||
<Button className="left pt-minimal" rightIconName={isOpen ? IconNames.CARET_DOWN : IconNames.CARET_RIGHT} onClick={() => this.setState({isOpen: !isOpen})}>
|
||||
<Button className="left" minimal rightIcon={isOpen ? IconNames.CARET_DOWN : IconNames.CARET_RIGHT} onClick={() => this.setState({isOpen: !isOpen})}>
|
||||
{RuleEditor.ruleToString(rule)}
|
||||
</Button>
|
||||
<div className="spacer"/>
|
||||
{moveUp ? <Button className="pt-minimal" iconName={IconNames.ARROW_UP} onClick={moveUp}/> : null}
|
||||
{moveDown ? <Button className="pt-minimal" iconName={IconNames.ARROW_DOWN} onClick={moveDown}/> : null}
|
||||
<Button className="pt-minimal" iconName={IconNames.TRASH} onClick={onDelete}/>
|
||||
{moveUp ? <Button minimal icon={IconNames.ARROW_UP} onClick={moveUp}/> : null}
|
||||
{moveDown ? <Button minimal icon={IconNames.ARROW_DOWN} onClick={moveDown}/> : null}
|
||||
<Button minimal icon={IconNames.TRASH} onClick={onDelete}/>
|
||||
</div>
|
||||
|
||||
<Collapse isOpen={isOpen}>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Button, Checkbox, Classes, Intent, Popover, Position } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from "axios";
|
||||
import * as ace from 'brace';
|
||||
import 'brace/ext/language_tools';
|
||||
|
@ -31,8 +32,6 @@ import * as ReactDOMServer from 'react-dom/server';
|
|||
import { SQLFunctionDoc } from "../../lib/sql-function-doc";
|
||||
import { AppToaster } from "../singletons/toaster";
|
||||
|
||||
import { IconNames } from './filler';
|
||||
|
||||
import './sql-control.scss';
|
||||
|
||||
const langTools = ace.acequire('ace/ext/language_tools');
|
||||
|
@ -205,11 +204,11 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
|
|||
}}
|
||||
/>
|
||||
<div className="buttons">
|
||||
<Button rightIconName={IconNames.CARET_RIGHT} onClick={() => onRun(query)}>
|
||||
<Button rightIcon={IconNames.CARET_RIGHT} onClick={() => onRun(query)}>
|
||||
{isRune ? 'Rune' : 'Run'}
|
||||
</Button>
|
||||
<Popover position={Position.BOTTOM_LEFT} content={autoCompletePopover}>
|
||||
<Button className={`${Classes.MINIMAL} pt-icon-more`}/>
|
||||
<Button minimal icon={IconNames.MORE}/>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>;
|
||||
|
|
|
@ -17,10 +17,9 @@
|
|||
*/
|
||||
|
||||
.table-column-selection {
|
||||
&.pt-popover-target{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// This will be mounted in a portal
|
||||
|
@ -28,7 +27,7 @@
|
|||
.form-group {
|
||||
margin-bottom: 0;
|
||||
|
||||
.pt-checkbox:last-child {
|
||||
.bp3-checkbox:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Checkbox, Menu, Popover, Position } from "@blueprintjs/core";
|
||||
import { Button, Checkbox, FormGroup, Menu, Popover, Position } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import * as React from 'react';
|
||||
|
||||
import { FormGroup, IconNames } from "./filler";
|
||||
|
||||
import "./table-column-selection.scss";
|
||||
|
||||
interface TableColumnSelectionProps extends React.Props<any> {
|
||||
|
@ -62,7 +61,7 @@ export class TableColumnSelection extends React.Component<TableColumnSelectionPr
|
|||
content={checkboxes}
|
||||
position={Position.BOTTOM_RIGHT}
|
||||
>
|
||||
<Button rightIconName={IconNames.CARET_DOWN} text={"Columns"} />
|
||||
<Button rightIcon={IconNames.CARET_DOWN} text={"Columns"} />
|
||||
</Popover>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Intent } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from 'axios';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
@ -61,7 +62,7 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
|
|||
|
||||
// Status works but SQL 405s => the SQL endpoint is disabled
|
||||
AppToaster.show({
|
||||
iconName: 'error',
|
||||
icon: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
timeout: 120000,
|
||||
/* tslint:disable:jsx-alignment */
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
*/
|
||||
|
||||
import { AnchorButton, Button, Classes, Dialog, Intent } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import * as React from 'react';
|
||||
|
||||
import { IconNames } from "../components/filler";
|
||||
import { DRUID_COMMUNITY, DRUID_DEVELOPER_GROUP, DRUID_USER_GROUP, DRUID_WEBSITE } from '../variables';
|
||||
|
||||
export interface AboutDialogProps extends React.Props<any> {
|
||||
|
@ -40,7 +40,7 @@ export class AboutDialog extends React.Component<AboutDialogProps, AboutDialogSt
|
|||
|
||||
/* tslint:disable:jsx-alignment */
|
||||
return <Dialog
|
||||
iconName={IconNames.GRAPH}
|
||||
icon={IconNames.GRAPH}
|
||||
onClose={onClose}
|
||||
title="Apache Druid"
|
||||
isOpen
|
||||
|
|
|
@ -18,15 +18,16 @@
|
|||
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Classes,
|
||||
Dialog,
|
||||
Intent,
|
||||
ProgressBar
|
||||
FormGroup,
|
||||
Icon, Intent, NumericInput, ProgressBar, TagInput
|
||||
} from "@blueprintjs/core";
|
||||
import { IconName } from "@blueprintjs/icons";
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ButtonGroup, FormGroup, Icon, NumericInput, TagInput } from '../components/filler';
|
||||
import { AppToaster } from '../singletons/toaster';
|
||||
|
||||
export interface AsyncAlertDialogProps extends React.Props<any> {
|
||||
|
@ -35,7 +36,7 @@ export interface AsyncAlertDialogProps extends React.Props<any> {
|
|||
confirmButtonText: string;
|
||||
cancelButtonText?: string;
|
||||
className?: string;
|
||||
icon?: string;
|
||||
icon?: IconName;
|
||||
intent?: Intent;
|
||||
successText: string;
|
||||
failText: string;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
.compaction-dialog {
|
||||
&.pt-dialog {
|
||||
&.bp3-dialog {
|
||||
top: 5%;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
*/
|
||||
|
||||
.coordinator-dynamic-config {
|
||||
&.pt-dialog {
|
||||
&.bp3-dialog {
|
||||
margin-top: 5vh;
|
||||
top: 5%;
|
||||
}
|
||||
|
||||
.pt-dialog-body {
|
||||
.bp3-dialog-body {
|
||||
max-height: 70vh;
|
||||
|
||||
.auto-form {
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
*/
|
||||
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import axios from 'axios';
|
||||
import * as React from 'react';
|
||||
|
||||
import { AutoForm } from '../components/auto-form';
|
||||
import { IconNames } from '../components/filler';
|
||||
import { AppToaster } from '../singletons/toaster';
|
||||
import { getDruidErrorMessage, QueryManager } from '../utils';
|
||||
|
||||
|
@ -74,7 +74,7 @@ export class CoordinatorDynamicConfigDialog extends React.Component<CoordinatorD
|
|||
config = configResp.data;
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
iconName: IconNames.ERROR,
|
||||
icon: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: `Could not load coordinator dynamic config: ${getDruidErrorMessage(e)}`
|
||||
});
|
||||
|
@ -97,7 +97,7 @@ export class CoordinatorDynamicConfigDialog extends React.Component<CoordinatorD
|
|||
});
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
iconName: IconNames.ERROR,
|
||||
icon: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: `Could not save coordinator dynamic config: ${getDruidErrorMessage(e)}`
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
.history-dialog {
|
||||
&.pt-dialog {
|
||||
&.bp3-dialog {
|
||||
width: 600px;
|
||||
top: 5%;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@
|
|||
margin: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.pt-card {
|
||||
.bp3-card {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Dialog } from "@blueprintjs/core";
|
||||
import { Card, Dialog } from "@blueprintjs/core";
|
||||
import * as React from "react";
|
||||
|
||||
import { Card, JSONCollapse } from "../components/filler";
|
||||
import { JSONCollapse } from '../components/json-collapse';
|
||||
|
||||
import "./history-dialog.scss";
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
|
||||
.lookup-edit-dialog {
|
||||
&.pt-dialog{
|
||||
&.bp3-dialog{
|
||||
top: 10vh;
|
||||
|
||||
width: 600px;
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Classes, Dialog, InputGroup, Intent } from "@blueprintjs/core";
|
||||
import { Button, Classes, Dialog, FormGroup, HTMLSelect, InputGroup, Intent } from "@blueprintjs/core";
|
||||
import * as React from "react";
|
||||
import AceEditor from "react-ace";
|
||||
|
||||
import { FormGroup } from "../components/filler";
|
||||
import { validJson } from "../utils";
|
||||
|
||||
import "./lookup-edit-dialog.scss";
|
||||
|
@ -69,15 +68,11 @@ export class LookupEditDialog extends React.Component<LookupEditDialogProps, Loo
|
|||
</FormGroup>;
|
||||
} else {
|
||||
return <FormGroup className={"lookup-label"} label={"Tier:"}>
|
||||
<div className="pt-select">
|
||||
<select disabled={isEdit} value={lookupTier} onChange={(e: any) => onChange("lookupEditTier", e.target.value)}>
|
||||
{
|
||||
allLookupTiers.map(tier => {
|
||||
return <option key={tier} value={tier}>{tier}</option>;
|
||||
})
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<HTMLSelect disabled={isEdit} value={lookupTier} onChange={(e: any) => onChange("lookupEditTier", e.target.value)}>
|
||||
{allLookupTiers.map(tier => (
|
||||
<option key={tier} value={tier}>{tier}</option>
|
||||
))}
|
||||
</HTMLSelect>
|
||||
</FormGroup>;
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +105,7 @@ export class LookupEditDialog extends React.Component<LookupEditDialogProps, Loo
|
|||
value={lookupVersion}
|
||||
onChange={(e: any) => onChange("lookupEditVersion", e.target.value)}
|
||||
placeholder={"Enter the lookup version"}
|
||||
rightElement={<Button className={"pt-minimal"} text={"Use ISO as version"} onClick={() => this.addISOVersion()} />}
|
||||
rightElement={<Button minimal text={"Use ISO as version"} onClick={() => this.addISOVersion()} />}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
*/
|
||||
|
||||
.overlord-dynamic-config {
|
||||
&.pt-dialog {
|
||||
&.bp3-dialog {
|
||||
margin-top: 5vh;
|
||||
top: 5%;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.pt-dialog-body {
|
||||
.bp3-dialog-body {
|
||||
max-height: 70vh;
|
||||
|
||||
.auto-form {
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
*/
|
||||
|
||||
import { Intent } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from "axios";
|
||||
import * as React from "react";
|
||||
|
||||
import { AutoForm } from "../components/auto-form";
|
||||
import { IconNames } from "../components/filler";
|
||||
import { AppToaster } from "../singletons/toaster";
|
||||
import { getDruidErrorMessage, QueryManager } from "../utils";
|
||||
|
||||
|
@ -76,7 +76,7 @@ export class OverlordDynamicConfigDialog extends React.Component<OverlordDynamic
|
|||
config = configResp.data || {};
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
iconName: IconNames.ERROR,
|
||||
icon: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: `Could not load overlord dynamic config: ${getDruidErrorMessage(e)}`
|
||||
});
|
||||
|
@ -99,7 +99,7 @@ export class OverlordDynamicConfigDialog extends React.Component<OverlordDynamic
|
|||
});
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
iconName: IconNames.ERROR,
|
||||
icon: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: `Could not save overlord dynamic config: ${getDruidErrorMessage(e)}`
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
.retention-dialog {
|
||||
&.pt-dialog {
|
||||
&.bp3-dialog {
|
||||
top: 5%;
|
||||
width: 750px;
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button } from "@blueprintjs/core";
|
||||
import { Button, FormGroup } from "@blueprintjs/core";
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import axios from 'axios';
|
||||
import * as React from 'react';
|
||||
|
||||
import { FormGroup, IconNames } from '../components/filler';
|
||||
import { Rule, RuleEditor } from '../components/rule-editor';
|
||||
import { QueryManager } from "../utils";
|
||||
|
||||
|
@ -183,7 +183,7 @@ export class RetentionDialog extends React.Component<RetentionDialogProps, Reten
|
|||
{(currentRules || []).map(this.renderRule)}
|
||||
</FormGroup>
|
||||
<FormGroup className="right">
|
||||
<Button iconName={IconNames.PLUS} onClick={this.addRule}>New rule</Button>
|
||||
<Button icon={IconNames.PLUS} onClick={this.addRule}>New rule</Button>
|
||||
</FormGroup>
|
||||
{
|
||||
(!currentRules.length && datasource !== '_default') &&
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.pt-select {
|
||||
&.pt-fill {
|
||||
flex: 1;
|
||||
.snitch-dialog {
|
||||
.bp3-dialog-footer-actions {
|
||||
position: relative;
|
||||
|
||||
.left-align-button {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin: 0 0 15px;
|
||||
}
|
|
@ -20,16 +20,19 @@ import {
|
|||
Button,
|
||||
Classes,
|
||||
Dialog,
|
||||
FormGroup,
|
||||
IDialogProps,
|
||||
InputGroup,
|
||||
Intent
|
||||
} from "@blueprintjs/core";
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import classNames = require('classnames');
|
||||
import * as React from 'react';
|
||||
|
||||
import { FormGroup, IconNames } from '../components/filler';
|
||||
|
||||
import { HistoryDialog } from "./history-dialog";
|
||||
|
||||
import './snitch-dialog.scss';
|
||||
|
||||
export interface SnitchDialogProps extends IDialogProps {
|
||||
onSave: (comment: string) => void;
|
||||
saveDisabled?: boolean;
|
||||
|
@ -106,7 +109,7 @@ export class SnitchDialog extends React.Component<SnitchDialogProps, SnitchDialo
|
|||
<div className={`dialog-body ${Classes.DIALOG_BODY}`}>
|
||||
<FormGroup label={"Why are you making this change?"} className={"comment"}>
|
||||
<InputGroup
|
||||
className="pt-large"
|
||||
large
|
||||
value={comment}
|
||||
placeholder={"Enter description here"}
|
||||
onChange={(e: any) => this.changeComment(e.target.value)}
|
||||
|
@ -127,7 +130,7 @@ export class SnitchDialog extends React.Component<SnitchDialogProps, SnitchDialo
|
|||
historyRecords={historyRecords}
|
||||
>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={this.back} iconName={IconNames.ARROW_LEFT}>Back</Button>
|
||||
<Button onClick={this.back} icon={IconNames.ARROW_LEFT}>Back</Button>
|
||||
</div>
|
||||
</HistoryDialog>;
|
||||
}
|
||||
|
@ -139,17 +142,17 @@ export class SnitchDialog extends React.Component<SnitchDialogProps, SnitchDialo
|
|||
return <div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
{showFinalStep || historyRecords === undefined
|
||||
? null
|
||||
: <Button style={{position: "absolute", left: "5px"}} className={"pt-minimal"} text="History" onClick={this.goToHistory}/>
|
||||
: <Button className="left-align-button" minimal text="History" onClick={this.goToHistory}/>
|
||||
}
|
||||
|
||||
{ showFinalStep
|
||||
? <Button onClick={this.back} iconName={IconNames.ARROW_LEFT}>Back</Button>
|
||||
? <Button onClick={this.back} icon={IconNames.ARROW_LEFT}>Back</Button>
|
||||
: onReset ? <Button onClick={this.reset} intent={"none" as any}>Reset</Button> : null
|
||||
}
|
||||
|
||||
{ showFinalStep
|
||||
? <Button disabled={saveDisabled} text="Save" onClick={this.save} intent={Intent.PRIMARY as any} rightIconName={IconNames.TICK}/>
|
||||
: <Button disabled={saveDisabled} text="Next" onClick={this.goToFinalStep} intent={Intent.PRIMARY as any} rightIconName={IconNames.ARROW_RIGHT}/>
|
||||
? <Button disabled={saveDisabled} text="Save" onClick={this.save} intent={Intent.PRIMARY as any} rightIcon={IconNames.TICK}/>
|
||||
: <Button disabled={saveDisabled} text="Next" onClick={this.goToFinalStep} intent={Intent.PRIMARY as any} rightIcon={IconNames.ARROW_RIGHT}/>
|
||||
}
|
||||
</div>;
|
||||
}
|
||||
|
@ -161,7 +164,9 @@ export class SnitchDialog extends React.Component<SnitchDialogProps, SnitchDialo
|
|||
if (showFinalStep) return this.renderFinalStep();
|
||||
if (showHistory) return this.renderHistoryDialog();
|
||||
|
||||
return <Dialog isOpen {...this.props}>
|
||||
const propsClone: any = Object.assign({}, this.props);
|
||||
propsClone.className = classNames('snitch-dialog', propsClone.className);
|
||||
return <Dialog isOpen {...propsClone}>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
.spec-dialog {
|
||||
&.pt-dialog {
|
||||
&.bp3-dialog {
|
||||
top: 5%;
|
||||
width: 90%;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
@import '../node_modules/normalize.css/normalize';
|
||||
@import '../lib/blueprint';
|
||||
@import '../node_modules/@blueprintjs/core/lib/css/blueprint';
|
||||
@import '../lib/react-table';
|
||||
|
||||
html,
|
||||
|
@ -29,7 +29,7 @@ body {
|
|||
}
|
||||
|
||||
body {
|
||||
&.pt-dark {
|
||||
&.bp3-dark {
|
||||
background: rgb(41, 55, 66);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, InputGroup, Intent } from '@blueprintjs/core';
|
||||
import { Button, HTMLSelect, InputGroup, Intent } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import * as numeral from "numeral";
|
||||
import * as React from 'react';
|
||||
import { Filter, FilterRender } from 'react-table';
|
||||
|
||||
import { HTMLSelect, IconNames } from "../components/filler";
|
||||
|
||||
export function addFilter(filters: Filter[], id: string, value: string): Filter[] {
|
||||
const currentFilter = filters.find(f => f.id === id);
|
||||
if (currentFilter) {
|
||||
|
@ -43,7 +42,7 @@ export function makeTextFilter(placeholder = ''): FilterRender {
|
|||
key={key}
|
||||
onChange={(e: any) => onChange(e.target.value)}
|
||||
value={filterValue}
|
||||
rightElement={filterValue && <Button iconName={IconNames.CROSS} className="pt-minimal" onClick={() => onChange('')} />}
|
||||
rightElement={filterValue && <Button icon={IconNames.CROSS} minimal onClick={() => onChange('')} />}
|
||||
placeholder={placeholder}
|
||||
/>;
|
||||
};
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
*/
|
||||
|
||||
import { Button, Intent, Switch } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from 'axios';
|
||||
import * as React from 'react';
|
||||
import ReactTable, { Filter } from "react-table";
|
||||
|
||||
import { IconNames } from "../components/filler";
|
||||
import { RuleEditor } from '../components/rule-editor';
|
||||
import { TableColumnSelection } from "../components/table-column-selection";
|
||||
import { AsyncActionDialog } from '../dialogs/async-action-dialog';
|
||||
|
@ -526,12 +526,12 @@ GROUP BY 1`);
|
|||
<div className="control-bar">
|
||||
<div className="control-label">Datasources</div>
|
||||
<Button
|
||||
iconName={IconNames.REFRESH}
|
||||
icon={IconNames.REFRESH}
|
||||
text="Refresh"
|
||||
onClick={() => this.datasourceQueryManager.rerunLastQuery()}
|
||||
/>
|
||||
<Button
|
||||
iconName={IconNames.APPLICATION}
|
||||
icon={IconNames.APPLICATION}
|
||||
text="Go to SQL"
|
||||
onClick={() => goToSql(this.datasourceQueryManager.getLastQuery())}
|
||||
/>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
color: inherit;
|
||||
}
|
||||
|
||||
.pt-card {
|
||||
.status-card {
|
||||
height: 160px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,19 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Card, H5, Icon } from "@blueprintjs/core";
|
||||
import { IconName, IconNames } from "@blueprintjs/icons";
|
||||
import axios from 'axios';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Card, H5, Icon, IconNames } from "../components/filler";
|
||||
import { getHeadProp, pluralIfNeeded, queryDruidSql, QueryManager } from '../utils';
|
||||
|
||||
import './home-view.scss';
|
||||
|
||||
export interface CardOptions {
|
||||
href: string;
|
||||
icon: string;
|
||||
icon: IconName;
|
||||
title: string;
|
||||
loading?: boolean;
|
||||
content: JSX.Element | string;
|
||||
|
@ -256,7 +257,7 @@ GROUP BY 1`);
|
|||
|
||||
renderCard(cardOptions: CardOptions): JSX.Element {
|
||||
return <a href={cardOptions.href} target={cardOptions.href[0] === '/' ? '_blank' : undefined}>
|
||||
<Card interactive>
|
||||
<Card className="status-card" interactive>
|
||||
<H5><Icon color="#bfccd5" icon={cardOptions.icon}/> {cardOptions.title}</H5>
|
||||
{cardOptions.loading ? <p>Loading...</p> : (cardOptions.error ? `Error: ${cardOptions.error}` : cardOptions.content)}
|
||||
</Card>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Button, Intent } from "@blueprintjs/core";
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import axios from 'axios';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
@ -132,7 +133,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
|
|||
} catch (e) {
|
||||
AppToaster.show(
|
||||
{
|
||||
iconName: 'error',
|
||||
icon: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: getDruidErrorMessage(e)
|
||||
}
|
||||
|
@ -202,7 +203,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
|
|||
} catch (e) {
|
||||
AppToaster.show(
|
||||
{
|
||||
iconName: 'error',
|
||||
icon: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: getDruidErrorMessage(e)
|
||||
}
|
||||
|
@ -222,8 +223,8 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
|
|||
if (lookupsError) {
|
||||
return <div className={"init-div"}>
|
||||
<Button
|
||||
iconName="build"
|
||||
text="Initialize Lookup"
|
||||
icon={IconNames.BUILD}
|
||||
text="Initialize lookup"
|
||||
onClick={() => this.initializeLookup()}
|
||||
/>
|
||||
</div>;
|
||||
|
@ -309,12 +310,12 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
|
|||
<div className="control-bar">
|
||||
<div className="control-label">Lookups</div>
|
||||
<Button
|
||||
iconName="refresh"
|
||||
icon={IconNames.REFRESH}
|
||||
text="Refresh"
|
||||
onClick={() => this.lookupsGetQueryManager.rerunLastQuery()}
|
||||
/>
|
||||
<Button
|
||||
iconName="plus"
|
||||
icon={IconNames.PLUS}
|
||||
text="Add"
|
||||
style={{display: this.state.lookupsError !== null ? 'none' : 'inline'}}
|
||||
onClick={() => this.openLookupEditDialog("", "")}
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
*/
|
||||
|
||||
import { Button, Intent } from "@blueprintjs/core";
|
||||
import { H5 } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from 'axios';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import ReactTable from "react-table";
|
||||
import { Filter } from "react-table";
|
||||
|
||||
import { H5, IconNames } from "../components/filler";
|
||||
import { TableColumnSelection } from "../components/table-column-selection";
|
||||
import { AppToaster } from "../singletons/toaster";
|
||||
import {
|
||||
|
@ -287,12 +288,12 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
|
|||
<div className="control-bar">
|
||||
<div className="control-label">Segments</div>
|
||||
<Button
|
||||
iconName={IconNames.REFRESH}
|
||||
icon={IconNames.REFRESH}
|
||||
text="Refresh"
|
||||
onClick={() => this.segmentsQueryManager.rerunLastQuery()}
|
||||
/>
|
||||
<Button
|
||||
iconName={IconNames.APPLICATION}
|
||||
icon={IconNames.APPLICATION}
|
||||
text="Go to SQL"
|
||||
onClick={() => goToSql(this.segmentsQueryManager.getLastQuery().query)}
|
||||
/>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Button, Switch } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from 'axios';
|
||||
import * as classNames from 'classnames';
|
||||
import { sum } from "d3-array";
|
||||
|
@ -24,7 +25,6 @@ import * as React from 'react';
|
|||
import ReactTable from "react-table";
|
||||
import { Filter } from "react-table";
|
||||
|
||||
import { IconNames } from '../components/filler';
|
||||
import { TableColumnSelection } from "../components/table-column-selection";
|
||||
import {
|
||||
addFilter,
|
||||
|
@ -373,12 +373,12 @@ WHERE "server_type" = 'historical'`);
|
|||
<div className="control-bar">
|
||||
<div className="control-label">Historicals</div>
|
||||
<Button
|
||||
iconName={IconNames.REFRESH}
|
||||
icon={IconNames.REFRESH}
|
||||
text="Refresh"
|
||||
onClick={() => this.serverQueryManager.rerunLastQuery()}
|
||||
/>
|
||||
<Button
|
||||
iconName={IconNames.APPLICATION}
|
||||
icon={IconNames.APPLICATION}
|
||||
text="Go to SQL"
|
||||
onClick={() => goToSql(this.serverQueryManager.getLastQuery())}
|
||||
/>
|
||||
|
@ -400,7 +400,7 @@ WHERE "server_type" = 'historical'`);
|
|||
<div className="control-bar">
|
||||
<div className="control-label">MiddleManagers</div>
|
||||
<Button
|
||||
iconName={IconNames.REFRESH}
|
||||
icon={IconNames.REFRESH}
|
||||
text="Refresh"
|
||||
onClick={() => this.middleManagerQueryManager.rerunLastQuery()}
|
||||
/>
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Alert, Button, Intent } from "@blueprintjs/core";
|
||||
import { Alert, Button, ButtonGroup, Intent, Label } from "@blueprintjs/core";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import axios from 'axios';
|
||||
import * as classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import ReactTable from "react-table";
|
||||
import { Filter } from "react-table";
|
||||
|
||||
import { ButtonGroup, IconNames, Label } from "../components/filler";
|
||||
import { TableColumnSelection } from "../components/table-column-selection";
|
||||
import { AsyncActionDialog } from "../dialogs/async-action-dialog";
|
||||
import { SpecDialog } from "../dialogs/spec-dialog";
|
||||
|
@ -568,12 +568,12 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
<div className="control-bar">
|
||||
<div className="control-label">Supervisors</div>
|
||||
<Button
|
||||
iconName={IconNames.REFRESH}
|
||||
icon={IconNames.REFRESH}
|
||||
text="Refresh"
|
||||
onClick={() => this.supervisorQueryManager.rerunLastQuery()}
|
||||
/>
|
||||
<Button
|
||||
iconName={IconNames.PLUS}
|
||||
icon={IconNames.PLUS}
|
||||
text="Submit supervisor"
|
||||
onClick={() => this.setState({ supervisorSpecDialogOpen: true })}
|
||||
/>
|
||||
|
@ -591,23 +591,23 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
<div className="control-label">Tasks</div>
|
||||
<Label>Group by</Label>
|
||||
<ButtonGroup>
|
||||
<Button className={classNames({ 'pt-active': groupTasksBy === null })} onClick={() => this.setState({ groupTasksBy: null })}>None</Button>
|
||||
<Button className={classNames({ 'pt-active': groupTasksBy === 'type' })} onClick={() => this.setState({ groupTasksBy: 'type' })}>Type</Button>
|
||||
<Button className={classNames({ 'pt-active': groupTasksBy === 'datasource' })} onClick={() => this.setState({ groupTasksBy: 'datasource' })}>Datasource</Button>
|
||||
<Button className={classNames({ 'pt-active': groupTasksBy === 'status' })} onClick={() => this.setState({ groupTasksBy: 'status' })}>Status</Button>
|
||||
<Button active={groupTasksBy === null} onClick={() => this.setState({ groupTasksBy: null })}>None</Button>
|
||||
<Button active={groupTasksBy === 'type'} onClick={() => this.setState({ groupTasksBy: 'type' })}>Type</Button>
|
||||
<Button active={groupTasksBy === 'datasource'} onClick={() => this.setState({ groupTasksBy: 'datasource' })}>Datasource</Button>
|
||||
<Button active={groupTasksBy === 'status'} onClick={() => this.setState({ groupTasksBy: 'status' })}>Status</Button>
|
||||
</ButtonGroup>
|
||||
<Button
|
||||
iconName={IconNames.REFRESH}
|
||||
icon={IconNames.REFRESH}
|
||||
text="Refresh"
|
||||
onClick={() => this.taskQueryManager.rerunLastQuery()}
|
||||
/>
|
||||
<Button
|
||||
iconName={IconNames.APPLICATION}
|
||||
icon={IconNames.APPLICATION}
|
||||
text="Go to SQL"
|
||||
onClick={() => goToSql(this.taskQueryManager.getLastQuery())}
|
||||
/>
|
||||
<Button
|
||||
iconName={IconNames.PLUS}
|
||||
icon={IconNames.PLUS}
|
||||
text="Submit task"
|
||||
onClick={() => this.setState({ taskSpecDialogOpen: true })}
|
||||
/>
|
||||
|
@ -629,7 +629,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
title="Submit task"
|
||||
/> : null }
|
||||
<Alert
|
||||
iconName={IconNames.ERROR}
|
||||
icon={IconNames.ERROR}
|
||||
intent={Intent.PRIMARY}
|
||||
isOpen={Boolean(alertErrorMsg)}
|
||||
confirmButtonText="OK"
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
<meta name="description" content="Apache Druid web console">
|
||||
<link rel="shortcut icon" href="/favicon.png">
|
||||
</head>
|
||||
<body class="pt-dark mouse-mode">
|
||||
<body class="bp3-dark mouse-mode">
|
||||
<div class="app-container"></div>
|
||||
<script src="public/web-console-0.14.0.js"></script>
|
||||
<script src="public/web-console-0.15.0.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue