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:
Vadim Ogievetsky 2019-04-01 23:06:48 -07:00 committed by Fangjin Yang
parent 1137bb2586
commit d2ff3eb199
45 changed files with 1537 additions and 581 deletions

View File

@ -66,7 +66,6 @@ public class RouterJettyServerInitializer implements JettyServerInitializer
"/",
"/coordinator-console/*",
"/public/*",
"/assets/*",
"/old-console/*",
"/pages/*",
"/unified-console.html",

View File

@ -2,7 +2,6 @@ node/
node_modules/
resources/
public/
assets/
lib/*.css
coordinator-console/

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -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

View File

@ -22,5 +22,4 @@ rm -rf \
coordinator-console \
pages \
public \
assets \
index.html

View File

@ -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"

View File

@ -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}/>;
}
}

View File

@ -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";

View File

@ -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>;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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>;
}
}

View File

@ -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
}}
/>;
}
}

View File

@ -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}>

View File

@ -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>;

View File

@ -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;
}
}

View File

@ -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>;
}
}

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -17,7 +17,7 @@
*/
.compaction-dialog {
&.pt-dialog {
&.bp3-dialog {
top: 5%;
}

View File

@ -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 {

View File

@ -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)}`
});

View File

@ -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;
}

View File

@ -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";

View File

@ -19,7 +19,7 @@
.lookup-edit-dialog {
&.pt-dialog{
&.bp3-dialog{
top: 10vh;
width: 600px;

View File

@ -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>

View File

@ -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 {

View File

@ -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)}`
});

View File

@ -17,7 +17,7 @@
*/
.retention-dialog {
&.pt-dialog {
&.bp3-dialog {
top: 5%;
width: 750px;
}

View File

@ -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') &&

View File

@ -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;
}

View File

@ -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>

View File

@ -17,7 +17,7 @@
*/
.spec-dialog {
&.pt-dialog {
&.bp3-dialog {
top: 5%;
width: 90%;
}

View File

@ -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);
}

View File

@ -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}
/>;
};

View File

@ -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())}
/>

View File

@ -28,7 +28,7 @@
color: inherit;
}
.pt-card {
.status-card {
height: 160px;
}
}

View File

@ -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}/>&nbsp;{cardOptions.title}</H5>
{cardOptions.loading ? <p>Loading...</p> : (cardOptions.error ? `Error: ${cardOptions.error}` : cardOptions.content)}
</Card>

View File

@ -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("", "")}

View File

@ -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)}
/>

View File

@ -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()}
/>

View File

@ -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"

View File

@ -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>