Commit c7187946 authored by Lijiao's avatar Lijiao Committed by GitHub
Browse files

Use office-fabric-ui components (#1964)

parent fdfff50d
...@@ -14,8 +14,9 @@ process.on('unhandledRejection', err => { ...@@ -14,8 +14,9 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read. // Ensure environment variables are read.
require('../config/env'); require('../config/env');
const fs = require('fs'); const fs = require('fs');
const chalk = require('chalk'); const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack'); const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server'); const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole'); const clearConsole = require('react-dev-utils/clearConsole');
...@@ -28,7 +29,7 @@ const { ...@@ -28,7 +29,7 @@ const {
} = require('react-dev-utils/WebpackDevServerUtils'); } = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser'); const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths'); const paths = require('../config/paths');
const config = require('../config/webpack.config.dev'); const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config'); const createDevServerConfig = require('../config/webpackDevServer.config');
const useYarn = fs.existsSync(paths.yarnLockFile); const useYarn = fs.existsSync(paths.yarnLockFile);
...@@ -54,35 +55,50 @@ if (process.env.HOST) { ...@@ -54,35 +55,50 @@ if (process.env.HOST) {
console.log( console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.` `If this was unintentional, check that you haven't mistakenly set it in your shell.`
); );
console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`); console.log(
console.log(); `Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
);
} }
// We attempt to use the default port but if it is busy, we offer the user to // We require that you explicitly set browsers and do not fall back to
// run on a different port. `choosePort()` Promise resolves to the next free port. // browserslist defaults.
choosePort(HOST, DEFAULT_PORT) const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
return choosePort(HOST, DEFAULT_PORT);
})
.then(port => { .then(port => {
if (port == null) { if (port == null) {
// We have not found a port. // We have not found a port.
return; return;
} }
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name; const appName = require(paths.appPackageJson).name;
const useTypeScript = fs.existsSync(paths.appTsConfig);
const urls = prepareUrls(protocol, HOST, port); const urls = prepareUrls(protocol, HOST, port);
const devSocket = {
warnings: warnings =>
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
errors: errors =>
devServer.sockWrite(devServer.sockets, 'errors', errors),
};
// Create a webpack compiler that is configured with custom messages. // Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({ const compiler = createCompiler({
appName, appName,
config, config,
//devSocket: null, devSocket,
urls, urls,
useYarn, useYarn,
//useTypeScript: true, useTypeScript,
webpack webpack,
}); });
// Load proxy config // Load proxy config
const proxySetting = require(paths.appPackageJson).proxy; const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic); const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// Serve webpack assets generated by the compiler over a web sever. // Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig( const serverConfig = createDevServerConfig(
proxyConfig, proxyConfig,
urls.lanUrlForConfig urls.lanUrlForConfig
...@@ -96,6 +112,18 @@ choosePort(HOST, DEFAULT_PORT) ...@@ -96,6 +112,18 @@ choosePort(HOST, DEFAULT_PORT)
if (isInteractive) { if (isInteractive) {
clearConsole(); clearConsole();
} }
// We used to support resolving modules according to `NODE_PATH`.
// This now has been deprecated in favor of jsconfig/tsconfig.json
// This lets you use absolute paths in imports inside large monorepos:
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
}
console.log(chalk.cyan('Starting the development server...\n')); console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser); openBrowser(urls.localUrlForBrowser);
}); });
...@@ -110,7 +138,6 @@ choosePort(HOST, DEFAULT_PORT) ...@@ -110,7 +138,6 @@ choosePort(HOST, DEFAULT_PORT)
.catch(err => { .catch(err => {
if (err && err.message) { if (err && err.message) {
console.log(err.message); console.log(err.message);
console.log(err.stack);
} }
process.exit(1); process.exit(1);
}); });
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'test';
process.env.NODE_ENV = 'test';
process.env.PUBLIC_URL = '';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const jest = require('jest');
const execSync = require('child_process').execSync;
let argv = process.argv.slice(2);
function isInGitRepository() {
try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
function isInMercurialRepository() {
try {
execSync('hg --cwd . root', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
// Watch unless on CI or explicitly running all tests
if (
!process.env.CI &&
argv.indexOf('--watchAll') === -1
) {
// https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
argv.push(hasSourceControl ? '--watch' : '--watchAll');
}
jest.run(argv);
...@@ -16,14 +16,30 @@ ...@@ -16,14 +16,30 @@
z-index: 1000; z-index: 1000;
} }
.headerCon{
width: 90%;
margin: 0 auto;
}
.contentBox{ .contentBox{
width: 100%; width: 100%;
} }
.content{ .content{
width: 86%; width: 89%;
min-width: 1024px; min-width: 1024px;
margin: 0 auto; margin: 0 auto;
margin-top: 74px; margin-top: 74px;
margin-bottom: 30px; margin-bottom: 30px;
background: #fff; background: #fff;
} }
/* office-fabric-ui */
.ms-Pivot-linkContent{
height: 44px;
}
.ms-Callout-main{
p{
font-weight: 500;
color: #333;
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
import * as React from 'react'; import * as React from 'react';
import { Row, Col } from 'antd'; import { Stack } from 'office-ui-fabric-react';
import { COLUMN } from './static/const'; import { COLUMN } from './static/const';
import { EXPERIMENT, TRIALS } from './static/datamodel'; import { EXPERIMENT, TRIALS } from './static/datamodel';
import './App.css'; import NavCon from './components/NavCon';
import SlideBar from './components/SlideBar'; import './App.scss';
interface AppState { interface AppState {
interval: number; interval: number;
columnList: Array<string>; columnList: string[];
experimentUpdateBroadcast: number; experimentUpdateBroadcast: number;
trialsUpdateBroadcast: number; trialsUpdateBroadcast: number;
metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed
} }
class App extends React.Component<{}, AppState> { class App extends React.Component<{}, AppState> {
private timerId: number | null; private timerId!: number | null;
constructor(props: {}) { constructor(props: {}) {
super(props); super(props);
...@@ -28,7 +28,7 @@ class App extends React.Component<{}, AppState> { ...@@ -28,7 +28,7 @@ class App extends React.Component<{}, AppState> {
} }
async componentDidMount(): Promise<void> { async componentDidMount(): Promise<void> {
await Promise.all([ EXPERIMENT.init(), TRIALS.init() ]); await Promise.all([EXPERIMENT.init(), TRIALS.init()]);
this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 }));
this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 }));
this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000); this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000);
...@@ -45,7 +45,7 @@ class App extends React.Component<{}, AppState> { ...@@ -45,7 +45,7 @@ class App extends React.Component<{}, AppState> {
} }
// TODO: use local storage // TODO: use local storage
changeColumn = (columnList: Array<string>): void => { changeColumn = (columnList: string[]): void => {
this.setState({ columnList: columnList }); this.setState({ columnList: columnList });
} }
...@@ -53,7 +53,7 @@ class App extends React.Component<{}, AppState> { ...@@ -53,7 +53,7 @@ class App extends React.Component<{}, AppState> {
this.setState({ metricGraphMode: val }); this.setState({ metricGraphMode: val });
} }
render(): React.ReactNode{ render(): React.ReactNode {
const { interval, columnList, experimentUpdateBroadcast, trialsUpdateBroadcast, metricGraphMode } = this.state; const { interval, columnList, experimentUpdateBroadcast, trialsUpdateBroadcast, metricGraphMode } = this.state;
if (experimentUpdateBroadcast === 0 || trialsUpdateBroadcast === 0) { if (experimentUpdateBroadcast === 0 || trialsUpdateBroadcast === 0) {
return null; // TODO: render a loading page return null; // TODO: render a loading page
...@@ -68,26 +68,25 @@ class App extends React.Component<{}, AppState> { ...@@ -68,26 +68,25 @@ class App extends React.Component<{}, AppState> {
metricGraphMode, changeMetricGraphMode: this.changeMetricGraphMode metricGraphMode, changeMetricGraphMode: this.changeMetricGraphMode
}) })
); );
return ( return (
<Row className="nni" style={{ minHeight: window.innerHeight }}> <Stack className="nni" style={{ minHeight: window.innerHeight }}>
<Row className="header"> <div className="header">
<Col span={1} /> <div className="headerCon">
<Col className="headerCon" span={22}> <NavCon changeInterval={this.changeInterval} refreshFunction={this.lastRefresh}/>
<SlideBar changeInterval={this.changeInterval} /> </div>
</Col> </div>
<Col span={1} /> <Stack className="contentBox">
</Row> <Stack className="content">
<Row className="contentBox">
<Row className="content">
{reactPropsChildren} {reactPropsChildren}
</Row> </Stack>
</Row> </Stack>
</Row> </Stack>
); );
} }
private refresh = async (): Promise<void> => { private refresh = async (): Promise<void> => {
const [ experimentUpdated, trialsUpdated ] = await Promise.all([ EXPERIMENT.update(), TRIALS.update() ]); const [experimentUpdated, trialsUpdated] = await Promise.all([EXPERIMENT.update(), TRIALS.update()]);
if (experimentUpdated) { if (experimentUpdated) {
this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 }));
} }
...@@ -95,7 +94,7 @@ class App extends React.Component<{}, AppState> { ...@@ -95,7 +94,7 @@ class App extends React.Component<{}, AppState> {
this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 })); this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 }));
} }
if ([ 'DONE', 'ERROR', 'STOPPED' ].includes(EXPERIMENT.status)) { if (['DONE', 'ERROR', 'STOPPED'].includes(EXPERIMENT.status)) {
// experiment finished, refresh once more to ensure consistency // experiment finished, refresh once more to ensure consistency
if (this.state.interval > 0) { if (this.state.interval > 0) {
this.setState({ interval: 0 }); this.setState({ interval: 0 });
...@@ -107,7 +106,7 @@ class App extends React.Component<{}, AppState> { ...@@ -107,7 +106,7 @@ class App extends React.Component<{}, AppState> {
} }
} }
private async lastRefresh(): Promise<void> { public async lastRefresh(): Promise<void> {
await EXPERIMENT.update(); await EXPERIMENT.update();
await TRIALS.update(true); await TRIALS.update(true);
this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 })); this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 }));
...@@ -116,3 +115,5 @@ class App extends React.Component<{}, AppState> { ...@@ -116,3 +115,5 @@ class App extends React.Component<{}, AppState> {
} }
export default App; export default App;
import * as React from 'react';
import { Icon, initializeIcons } from 'office-ui-fabric-react';
initializeIcons();
const infoIcon = <Icon iconName='info' />;
const warining = <Icon iconName='Warning' />;
const errorBadge = <Icon iconName='ErrorBadge' />;
const completed = <Icon iconName='Completed' />;
const blocked = <Icon iconName='StatusCircleBlock' />;
const copy = <Icon iconName='Copy' />;
const tableListIcon = <Icon iconName='BulletedList' />;
const downLoadIcon = { iconName: 'Download' };
const infoIconAbout = { iconName: 'info' };
const timeIcon = { iconName: 'ReminderTime' };
const disableUpdates = { iconName: 'DisableUpdates' };
const requency = { iconName: 'Timer' };
const closeTimer = { iconName: 'Blocked2' };
const LineChart = <Icon iconName='LineChart' />;
export { infoIcon, warining, errorBadge, completed, blocked,
infoIconAbout, copy, tableListIcon, downLoadIcon, timeIcon,
disableUpdates, requency, closeTimer, LineChart
};
import { getTheme, mergeStyleSets, FontWeights, FontSizes } from 'office-ui-fabric-react';
// Themed styles for the example.
const theme = getTheme();
const contentStyles = mergeStyleSets({
container: {
display: 'flex',
flexFlow: 'column nowrap',
alignItems: 'stretch'
},
header: [
theme.fonts.xLargePlus,
{
flex: '1 1 auto',
borderTop: `4px solid ${theme.palette.themePrimary}`,
color: theme.palette.neutralPrimary,
display: 'flex',
fontSize: FontSizes.xLarge,
alignItems: 'center',
fontWeight: FontWeights.semibold,
padding: '12px 12px 14px 24px'
}
],
body: {
flex: '4 4 auto',
padding: '0 24px 24px 24px',
overflowY: 'hidden',
selectors: {
p: {
margin: '14px 0'
},
'p:first-child': {
marginTop: 0
},
'p:last-child': {
marginBottom: 0
}
}
}
});
const iconButtonStyles = mergeStyleSets({
root: {
color: theme.palette.neutralPrimary,
marginLeft: 'auto',
marginTop: '4px',
marginRight: '2px'
},
rootHovered: {
color: theme.palette.neutralDark
}
});
export { contentStyles, iconButtonStyles };
\ No newline at end of file
import * as React from 'react';
import { Dialog, DialogType, DialogFooter, Checkbox, PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import { OPERATION } from '../../static/const';
interface ChangeColumnState {
userSelectColumnList: string[];
originSelectColumnList: string[];
}
interface ChangeColumnProps {
isHideDialog: boolean;
showColumn: string[]; // all column List
selectedColumn: string[]; // user selected column list
changeColumn: (val: string[]) => void;
hideShowColumnDialog: () => void;
}
interface CheckBoxItems {
label: string;
checked: boolean;
onChange: () => void;
}
class ChangeColumnComponent extends React.Component<ChangeColumnProps, ChangeColumnState> {
constructor(props: ChangeColumnProps) {
super(props);
this.state = { userSelectColumnList: this.props.selectedColumn, originSelectColumnList: this.props.selectedColumn };
}
makeChangeHandler = (label: string): any => {
return (ev: any, checked: boolean): void => this.onCheckboxChange(ev, label, checked);
}
onCheckboxChange = (ev: React.FormEvent<HTMLElement | HTMLInputElement> | undefined, label: string, val?: boolean, ): void => {
const source: string[] = JSON.parse(JSON.stringify(this.state.userSelectColumnList));
if (val === true) {
if (!source.includes(label)) {
source.push(label);
this.setState(() => ({ userSelectColumnList: source }));
}
} else {
if (source.includes(label)) {
// remove from source
const result = source.filter((item) => item !== label);
this.setState(() => ({ userSelectColumnList: result }));
}
}
};
saveUserSelectColumn = (): void => {
const { userSelectColumnList } = this.state;
const { showColumn } = this.props;
// sort by Trial No. | ID | Duration | Start Time | End Time | ...
const sortColumn: string[] = [];
/**
*
* TODO: use this function to refactor sort column
* search space might orderless
showColumn.map(item => {
userSelectColumnList.map(key => {
if (item === key || key.includes('search space')) {
if (!sortColumn.includes(key)) {
sortColumn.push(key);
}
}
});
});
*/
// push ![Operation] ![search space] column
showColumn.map(item => {
userSelectColumnList.map(key => {
if (item === key && item !== OPERATION) {
sortColumn.push(key);
}
});
});
// push search space key
userSelectColumnList.map(index => {
if (index.includes('search space')) {
if (!sortColumn.includes(index)) {
sortColumn.push(index);
}
}
});
// push Operation
if (userSelectColumnList.includes(OPERATION)) {
sortColumn.push(OPERATION);
}
this.props.changeColumn(sortColumn);
this.hideDialog(); // hide dialog
}
hideDialog = (): void => {
this.props.hideShowColumnDialog();
}
// user exit dialog
cancelOption = (): void => {
// reset select column
const { originSelectColumnList } = this.state;
this.setState({ userSelectColumnList: originSelectColumnList }, () => {
this.hideDialog();
});
}
render(): React.ReactNode {
const { showColumn, isHideDialog } = this.props;
const { userSelectColumnList } = this.state;
const renderOptions: Array<CheckBoxItems> = [];
showColumn.map(item => {
if (userSelectColumnList.includes(item)) {
// selected column name
renderOptions.push({ label: item, checked: true, onChange: this.makeChangeHandler(item) });
} else {
renderOptions.push({ label: item, checked: false, onChange: this.makeChangeHandler(item) });
}
});
return (
<div>
<div>Hello</div>
<Dialog
hidden={isHideDialog} // required field!
dialogContentProps={{
type: DialogType.largeHeader,
title: 'Change table column',
subText: 'You can chose which columns you want to see in the table.'
}}
modalProps={{
isBlocking: false,
styles: { main: { maxWidth: 450 } }
}}
>
<div>
{renderOptions.map(item => {
return <Checkbox key={item.label} {...item} styles={{ root: { marginBottom: 8 } }} />
})}
</div>
<DialogFooter>
<PrimaryButton text="Save" onClick={this.saveUserSelectColumn} />
<DefaultButton text="Cancel" onClick={this.cancelOption} />
</DialogFooter>
</Dialog>
</div>
);
}
}
export default ChangeColumnComponent;
\ No newline at end of file
import * as React from 'react'; import * as React from 'react';
import { Row, Modal } from 'antd'; import { Stack, Modal, IconButton } from 'office-ui-fabric-react';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
import IntermediateVal from '../public-child/IntermediateVal'; import IntermediateVal from '../public-child/IntermediateVal';
import { TRIALS } from '../../static/datamodel'; import { TRIALS } from '../../static/datamodel';
import { contentStyles, iconButtonStyles } from '../Buttons/ModalTheme';
import '../../static/style/compare.scss'; import '../../static/style/compare.scss';
import { TableRecord, Intermedia, TooltipForIntermediate } from 'src/static/interface'; import { TableRecord, Intermedia, TooltipForIntermediate } from '../../static/interface'; // eslint-disable-line no-unused-vars
// the modal of trial compare // the modal of trial compare
interface CompareProps { interface CompareProps {
compareRows: Array<TableRecord>; compareStacks: Array<TableRecord>;
visible: boolean;
cancelFunc: () => void; cancelFunc: () => void;
} }
class Compare extends React.Component<CompareProps, {}> { class Compare extends React.Component<CompareProps, {}> {
public _isCompareMount: boolean; public _isCompareMount!: boolean;
constructor(props: CompareProps) { constructor(props: CompareProps) {
super(props); super(props);
} }
intermediate = (): any => { intermediate = (): React.ReactNode => {
const { compareRows } = this.props; const { compareStacks } = this.props;
const trialIntermediate: Array<Intermedia> = []; const trialIntermediate: Array<Intermedia> = [];
const idsList: Array<string> = []; const idsList: string[] = [];
Object.keys(compareRows).map(item => { compareStacks.forEach(element => {
const temp = compareRows[item]; const trial = TRIALS.getTrial(element.id);
const trial = TRIALS.getTrial(temp.id);
trialIntermediate.push({ trialIntermediate.push({
name: temp.id, name: element.id,
data: trial.description.intermediate, data: trial.description.intermediate,
type: 'line', type: 'line',
hyperPara: trial.description.parameters hyperPara: trial.description.parameters
}); });
idsList.push(temp.id); idsList.push(element.id);
}); });
// find max intermediate number // find max intermediate number
trialIntermediate.sort((a, b) => { return (b.data.length - a.data.length); }); trialIntermediate.sort((a, b) => { return (b.data.length - a.data.length); });
const legend: Array<string> = []; const legend: string[] = [];
// max length // max length
const length = trialIntermediate[0] !== undefined ? trialIntermediate[0].data.length : 0; const length = trialIntermediate[0] !== undefined ? trialIntermediate[0].data.length : 0;
const xAxis: number[] = []; const xAxis: number[] = [];
Object.keys(trialIntermediate).map(item => { trialIntermediate.forEach(element => {
const temp = trialIntermediate[item]; legend.push(element.name);
legend.push(temp.name);
}); });
for (let i = 1; i <= length; i++) { for (let i = 1; i <= length; i++) {
xAxis.push(i); xAxis.push(i);
...@@ -59,7 +57,7 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -59,7 +57,7 @@ class Compare extends React.Component<CompareProps, {}> {
return [point[0] - 300, 80]; return [point[0] - 300, 80];
} }
}, },
formatter: function (data: TooltipForIntermediate): any { formatter: function (data: TooltipForIntermediate): React.ReactNode {
const trialId = data.seriesName; const trialId = data.seriesName;
let obj = {}; let obj = {};
const temp = trialIntermediate.find(key => key.name === trialId); const temp = trialIntermediate.find(key => key.name === trialId);
...@@ -108,19 +106,18 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -108,19 +106,18 @@ class Compare extends React.Component<CompareProps, {}> {
// render table column --- // render table column ---
initColumn = (): React.ReactNode => { initColumn = (): React.ReactNode => {
const idList: Array<string> = []; const idList: string[] = [];
const sequenceIdList: number[] = []; const sequenceIdList: number[] = [];
const durationList: number[] = []; const durationList: number[] = [];
const compareRows = this.props.compareRows.map(tableRecord => TRIALS.getTrial(tableRecord.id)); const compareStacks = this.props.compareStacks.map(tableRecord => TRIALS.getTrial(tableRecord.id));
const parameterList: Array<object> = []; const parameterList: Array<object> = [];
let parameterKeys: Array<string> = []; let parameterKeys: string[] = [];
if (compareRows.length !== 0) { if (compareStacks.length !== 0) {
parameterKeys = Object.keys(compareRows[0].description.parameters); parameterKeys = Object.keys(compareStacks[0].description.parameters);
} }
Object.keys(compareRows).map(item => { compareStacks.forEach(temp => {
const temp = compareRows[item];
idList.push(temp.id); idList.push(temp.id);
sequenceIdList.push(temp.sequenceId); sequenceIdList.push(temp.sequenceId);
durationList.push(temp.duration); durationList.push(temp.duration);
...@@ -132,7 +129,7 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -132,7 +129,7 @@ class Compare extends React.Component<CompareProps, {}> {
? true : false; ? true : false;
} }
return ( return (
<table className="compare"> <table className="compare-modal-table">
<tbody> <tbody>
<tr> <tr>
<td className="column">Id</td> <td className="column">Id</td>
...@@ -152,8 +149,8 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -152,8 +149,8 @@ class Compare extends React.Component<CompareProps, {}> {
</tr> </tr>
<tr> <tr>
<td className="column">Default metric</td> <td className="column">Default metric</td>
{Object.keys(compareRows).map(index => { {Object.keys(compareStacks).map(index => {
const temp = compareRows[index]; const temp = compareStacks[index];
return ( return (
<td className="value" key={index}> <td className="value" key={index}>
<IntermediateVal trialId={temp.id} /> <IntermediateVal trialId={temp.id} />
...@@ -204,24 +201,31 @@ class Compare extends React.Component<CompareProps, {}> { ...@@ -204,24 +201,31 @@ class Compare extends React.Component<CompareProps, {}> {
this._isCompareMount = false; this._isCompareMount = false;
} }
render(): React.ReactNode{ render(): React.ReactNode {
const { visible, cancelFunc } = this.props; const { cancelFunc } = this.props;
return ( return (
<Modal <Modal
title="Compare trials" isOpen={true}
visible={visible} containerClassName={contentStyles.container}
onCancel={cancelFunc} className="compare-modal"
footer={null}
destroyOnClose={true}
maskClosable={false}
width="90%"
> >
<Row className="compare-intermediate"> <div>
<div className={contentStyles.header}>
<span>Compare trials</span>
<IconButton
styles={iconButtonStyles}
iconProps={{ iconName: 'Cancel' }}
ariaLabel="Close popup modal"
onClick={cancelFunc}
/>
</div>
<Stack className="compare-modal-intermediate">
{this.intermediate()} {this.intermediate()}
<Row className="compare-yAxis"># Intermediate result</Row> <Stack className="compare-yAxis"># Intermediate result</Stack>
</Row> </Stack>
<Row>{this.initColumn()}</Row> <Stack>{this.initColumn()}</Stack>
</div>
</Modal> </Modal>
); );
} }
......
import * as React from 'react'; import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import { Row, Col, Input, Modal, Form, Button, Icon } from 'antd'; import { Stack, StackItem, PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { MANAGER_IP } from '../../static/const'; import { MANAGER_IP } from '../../static/const';
import { EXPERIMENT, TRIALS } from '../../static/datamodel'; import { EXPERIMENT, TRIALS } from '../../static/datamodel';
import { FormComponentProps } from 'antd/lib/form'; import { warining, errorBadge, completed } from '../Buttons/Icon';
const FormItem = Form.Item;
import './customized.scss'; import './customized.scss';
interface CustomizeProps extends FormComponentProps { interface CustomizeProps {
visible: boolean; visible: boolean;
copyTrialId: string; copyTrialId: string;
closeCustomizeModal: () => void; closeCustomizeModal: () => void;
...@@ -21,6 +21,7 @@ interface CustomizeState { ...@@ -21,6 +21,7 @@ interface CustomizeState {
copyTrialParameter: object; // user click the trial's parameters copyTrialParameter: object; // user click the trial's parameters
customParameters: object; // customized trial, maybe user change trial's parameters customParameters: object; // customized trial, maybe user change trial's parameters
customID: number; // submit customized trial succeed, return the new customized trial id customID: number; // submit customized trial succeed, return the new customized trial id
changeMap: Map<string, string | number>; // store change key: value
} }
class Customize extends React.Component<CustomizeProps, CustomizeState> { class Customize extends React.Component<CustomizeProps, CustomizeState> {
...@@ -34,15 +35,29 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> { ...@@ -34,15 +35,29 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
searchSpace: EXPERIMENT.searchSpace, searchSpace: EXPERIMENT.searchSpace,
copyTrialParameter: {}, copyTrialParameter: {},
customParameters: {}, customParameters: {},
customID: NaN customID: NaN,
changeMap: new Map
}; };
} }
getFinalVal = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { name, value } = event.target;
const { changeMap } = this.state;
this.setState({ changeMap: changeMap.set(name, value) });
}
// [submit click] user add a new trial [submit a trial] // [submit click] user add a new trial [submit a trial]
addNewTrial = (): void => { addNewTrial = (): void => {
const { searchSpace, copyTrialParameter } = this.state; const { searchSpace, copyTrialParameter, changeMap } = this.state;
// get user edited hyperParameter, ps: will change data type if you modify the input val // get user edited hyperParameter, ps: will change data type if you modify the input val
const customized = this.props.form.getFieldsValue(); const customized = JSON.parse(JSON.stringify(copyTrialParameter));
// changeMap: user changed keys: values
changeMap.forEach(function (value, key) {
customized[key] = value;
});
// true: parameters are wrong // true: parameters are wrong
let flag = false; let flag = false;
Object.keys(customized).map(item => { Object.keys(customized).map(item => {
...@@ -113,13 +128,13 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> { ...@@ -113,13 +128,13 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
closeSucceedHint = (): void => { closeSucceedHint = (): void => {
// also close customized trial modal // also close customized trial modal
this.setState(() => ({ isShowSubmitSucceed: false })); this.setState(() => ({ isShowSubmitSucceed: false, changeMap: new Map() }));
this.props.closeCustomizeModal(); this.props.closeCustomizeModal();
} }
closeFailedHint = (): void => { closeFailedHint = (): void => {
// also close customized trial modal // also close customized trial modal
this.setState(() => ({ isShowSubmitFailed: false })); this.setState(() => ({ isShowSubmitFailed: false, changeMap: new Map() }));
this.props.closeCustomizeModal(); this.props.closeCustomizeModal();
} }
...@@ -142,176 +157,116 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> { ...@@ -142,176 +157,116 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
render(): React.ReactNode { render(): React.ReactNode {
const { closeCustomizeModal, visible } = this.props; const { closeCustomizeModal, visible } = this.props;
const { isShowSubmitSucceed, isShowSubmitFailed, isShowWarning, customID, copyTrialParameter } = this.state; const { isShowSubmitSucceed, isShowSubmitFailed, isShowWarning, customID, copyTrialParameter } = this.state;
const {
form: { getFieldDecorator },
// form: { getFieldDecorator, getFieldValue },
} = this.props;
const warning = 'The parameters you set are not in our search space, this may cause the tuner to crash, Are' const warning = 'The parameters you set are not in our search space, this may cause the tuner to crash, Are'
+ ' you sure you want to continue submitting?'; + ' you sure you want to continue submitting?';
return ( return (
<Row> <Stack>
{/* form: search space */} <Dialog
<Modal hidden={!visible} // required field!
title="Customized trial setting" dialogContentProps={{
visible={visible} type: DialogType.largeHeader,
onCancel={closeCustomizeModal} title: 'Customized trial setting',
footer={null} subText: 'You can submit a customized trial.'
destroyOnClose={true} }}
maskClosable={false} modalProps={{
centered={true} isBlocking: false,
styles: { main: { maxWidth: 450 } }
}}
> >
{/* search space form */} <form className="hyper-box">
<Row className="hyper-box">
<Form>
{ {
Object.keys(copyTrialParameter).map(item => ( Object.keys(copyTrialParameter).map(item => (
<Row key={item} className="hyper-form"> <Stack horizontal key={item} className="hyper-form">
<Col span={9} className="title">{item}</Col> <StackItem styles={{ root: { minWidth: 100 } }} className="title">{item}</StackItem>
<Col span={15} className="inputs"> <StackItem className="inputs">
<FormItem key={item} style={{ marginBottom: 0 }}> <input
{getFieldDecorator(item, { type="text"
initialValue: copyTrialParameter[item], name={item}
})( defaultValue={copyTrialParameter[item]}
<Input /> onChange={this.getFinalVal}
)} />
</FormItem> </StackItem>
</Col> </Stack>
</Row>
) )
) )
} }
<Row key="tag" className="hyper-form tag-input"> {/* disable [tag] because we havn't support */}
<Col span={9} className="title">Tag</Col> {/* <Stack key="tag" horizontal className="hyper-form tag-input">
<Col span={15} className="inputs"> <StackItem grow={9} className="title">Tag</StackItem>
<FormItem key="tag" style={{ marginBottom: 0 }}> <StackItem grow={15} className="inputs">
{getFieldDecorator('tag', { <input type="text" value='Customized' />
initialValue: 'Customized', </StackItem>
})( </Stack> */}
<Input /> </form>
)} <DialogFooter>
</FormItem> <PrimaryButton text="Submit" onClick={this.addNewTrial} />
</Col> <DefaultButton text="Cancel" onClick={closeCustomizeModal} />
</Row> </DialogFooter>
</Form> </Dialog>
</Row>
<Row className="modal-button">
<Button
type="primary"
className="tableButton distance"
onClick={this.addNewTrial}
>
Submit
</Button>
<Button
className="tableButton cancelSty"
onClick={this.props.closeCustomizeModal}
>
Cancel
</Button>
</Row>
{/* control button */}
</Modal>
{/* clone: prompt succeed or failed */} {/* clone: prompt succeed or failed */}
<Modal <Dialog
visible={isShowSubmitSucceed} hidden={!isShowSubmitSucceed}
footer={null} onDismiss={this.closeSucceedHint}
destroyOnClose={true} dialogContentProps={{
maskClosable={false} type: DialogType.normal,
closable={false} title: <div className="icon-color">{completed}<b>Submit successfully</b></div>,
centered={true} closeButtonAriaLabel: 'Close',
> subText: `You can find your customized trial by Trial No.${customID}`
<Row className="resubmit"> }}
<Row> modalProps={{
<h2 className="title"> isBlocking: false,
<span> styles: { main: { minWidth: 500 } },
<Icon type="check-circle" className="color-succ" /> }}
<b>Submit successfully</b>
</span>
</h2>
<div className="hint">
<span>You can find your customized trial by Trial No.{customID}</span>
</div>
</Row>
<Row className="modal-button">
<Button
className="tableButton cancelSty"
onClick={this.closeSucceedHint}
> >
OK <DialogFooter>
</Button> <PrimaryButton onClick={this.closeSucceedHint} text="OK" />
</Row> </DialogFooter>
</Row> </Dialog>
</Modal>
<Modal <Dialog
visible={isShowSubmitFailed} hidden={!isShowSubmitFailed}
footer={null} onDismiss={this.closeSucceedHint}
destroyOnClose={true} dialogContentProps={{
maskClosable={false} type: DialogType.normal,
closable={false} title: <div className="icon-error">{errorBadge}Submit Failed</div>,
centered={true} closeButtonAriaLabel: 'Close',
> subText: 'Unknown error.'
<Row className="resubmit"> }}
<Row> modalProps={{
<h2 className="title"> isBlocking: false,
<span> styles: { main: { minWidth: 500 } },
<Icon type="check-circle" className="color-error" />Submit Failed }}
</span>
</h2>
<div className="hint">
<span>Unknown error.</span>
</div>
</Row>
<Row className="modal-button">
<Button
className="tableButton cancelSty"
onClick={this.closeFailedHint}
> >
OK <DialogFooter>
</Button> <PrimaryButton onClick={this.closeFailedHint} text="OK" />
</Row> </DialogFooter>
</Row> </Dialog>
</Modal>
{/* hyperParameter not match search space, warning modal */} {/* hyperParameter not match search space, warning modal */}
<Modal <Dialog
visible={isShowWarning} hidden={!isShowWarning}
footer={null} onDismiss={this.closeSucceedHint}
destroyOnClose={true} dialogContentProps={{
maskClosable={false} type: DialogType.normal,
closable={false} title: <div className="icon-error">{warining}Warning</div>,
centered={true} closeButtonAriaLabel: 'Close',
> subText: `${warning}`
<Row className="resubmit"> }}
<Row> modalProps={{
<h2 className="title"> isBlocking: false,
<span> styles: { main: { minWidth: 500 } },
<Icon className="color-warn" type="warning" />Warning }}
</span>
</h2>
<div className="hint">
<span>{warning}</span>
</div>
</Row>
<Row className="modal-button center">
<Button
className="tableButton cancelSty distance"
onClick={this.warningConfirm}
> >
Confirm <DialogFooter>
</Button> <PrimaryButton onClick={this.warningConfirm} text="Confirm" />
<Button <DefaultButton onClick={this.warningCancel} text="Cancel" />
className="tableButton cancelSty" </DialogFooter>
onClick={this.warningCancel} </Dialog>
> </Stack>
Cancel
</Button>
</Row>
</Row>
</Modal>
</Row>
); );
} }
} }
export default Form.create<FormComponentProps>()(Customize); export default Customize;
\ No newline at end of file \ No newline at end of file
import * as React from 'react'; import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import { downFile } from '../../static/function'; import { downFile } from '../../static/function';
import { Drawer, Tabs, Row, Col, Button } from 'antd'; import {
Stack, PrimaryButton, DefaultButton, Panel, StackItem, Pivot, PivotItem
} from 'office-ui-fabric-react';
import { MANAGER_IP, DRAWEROPTION } from '../../static/const'; import { MANAGER_IP, DRAWEROPTION } from '../../static/const';
import MonacoEditor from 'react-monaco-editor'; import MonacoEditor from 'react-monaco-editor';
const { TabPane } = Tabs;
import '../../static/style/logDrawer.scss'; import '../../static/style/logDrawer.scss';
interface ExpDrawerProps { interface ExpDrawerProps {
...@@ -19,13 +20,13 @@ interface ExpDrawerState { ...@@ -19,13 +20,13 @@ interface ExpDrawerState {
class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> { class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> {
public _isCompareMount: boolean; public _isCompareMount!: boolean;
constructor(props: ExpDrawerProps) { constructor(props: ExpDrawerProps) {
super(props); super(props);
this.state = { this.state = {
experiment: '', experiment: '',
expDrawerHeight: window.innerHeight - 48 expDrawerHeight: window.innerHeight
}; };
} }
...@@ -72,7 +73,7 @@ class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> { ...@@ -72,7 +73,7 @@ class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> {
} }
onWindowResize = (): void => { onWindowResize = (): void => {
this.setState(() => ({expDrawerHeight: window.innerHeight - 48})); this.setState(() => ({ expDrawerHeight: window.innerHeight }));
} }
componentDidMount(): void { componentDidMount(): void {
...@@ -97,53 +98,42 @@ class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> { ...@@ -97,53 +98,42 @@ class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> {
const { isVisble, closeExpDrawer } = this.props; const { isVisble, closeExpDrawer } = this.props;
const { experiment, expDrawerHeight } = this.state; const { experiment, expDrawerHeight } = this.state;
return ( return (
<Row className="logDrawer"> <Stack className="logDrawer">
<Drawer <Panel
// title="Log Message" isOpen={isVisble}
placement="right" hasCloseButton={false}
closable={false} styles={{ root: { height: expDrawerHeight, paddingTop: 15 } }}
destroyOnClose={true}
onClose={closeExpDrawer}
visible={isVisble}
width="54%"
height={expDrawerHeight}
> >
{/* 104: tabHeight(40) + tabMarginBottom(16) + buttonHeight(32) + buttonMarginTop(16) */} <Pivot style={{ minHeight: 190 }} className="log-tab-body">
<div className="card-container log-tab-body"> <PivotItem headerText="Experiment Parameters">
<Tabs type="card" style={{ height: expDrawerHeight, minHeight: 190 }}>
<TabPane tab="Experiment Parameters" key="Experiment">
<div className="just-for-log"> <div className="just-for-log">
<MonacoEditor <MonacoEditor
width="100%" width="100%"
height={expDrawerHeight - 104} // 92 + marginTop[16]
height={expDrawerHeight - 108}
language="json" language="json"
value={experiment} value={experiment}
options={DRAWEROPTION} options={DRAWEROPTION}
/> />
</div> </div>
<Row className="buttons"> <Stack horizontal className="buttons">
<Col span={12} className="download"> <StackItem grow={50} className="download">
<Button <PrimaryButton
type="primary" text="Download"
onClick={this.downExperimentParameters} onClick={this.downExperimentParameters}
> />
Download </StackItem>
</Button> <StackItem grow={50} className="close">
</Col> <DefaultButton
<Col span={12} className="close"> text="Close"
<Button
type="default"
onClick={closeExpDrawer} onClick={closeExpDrawer}
> />
Close </StackItem>
</Button> </Stack>
</Col> </PivotItem>
</Row> </Pivot>
</TabPane> </Panel>
</Tabs> </Stack>
</div>
</Drawer>
</Row>
); );
} }
} }
......
import * as React from 'react';
import {
Stack, FocusTrapCallout, DefaultButton,
FocusZone,
PrimaryButton, getTheme, mergeStyleSets, FontWeights
} from 'office-ui-fabric-react';
import { killJob } from '../../static/function';
import { blocked } from '../Buttons/Icon';
const theme = getTheme();
const styles = mergeStyleSets({
buttonArea: {
verticalAlign: 'top',
display: 'inline-block',
textAlign: 'center',
height: 32
},
callout: {
maxWidth: 300
},
header: {
padding: '18px 24px 12px'
},
title: [
theme.fonts.xLarge,
{
margin: 0,
color: theme.palette.neutralPrimary,
fontWeight: FontWeights.semilight
}
],
inner: {
height: '100%',
padding: '0 24px 20px'
},
actions: {
position: 'relative',
marginTop: 20,
width: '100%',
whiteSpace: 'nowrap'
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
padding: '0 24px 24px'
},
subtext: [
theme.fonts.small,
{
margin: 0,
color: theme.palette.neutralPrimary,
fontWeight: FontWeights.semilight
}
]
});
interface KillJobState {
isCalloutVisible: boolean;
}
interface KillJobProps {
trial: any;
}
class KillJob extends React.Component<KillJobProps, KillJobState> {
private menuButtonElement!: HTMLElement | null;
constructor(props: KillJobProps) {
super(props);
this.state = { isCalloutVisible: false };
}
onDismiss = (): void => {
this.setState(() => ({ isCalloutVisible: false }));
}
onKill = (): void => {
this.setState({ isCalloutVisible: false }, () => {
const { trial } = this.props;
killJob(trial.key, trial.id, trial.status);
});
}
openPromot = (event: React.SyntheticEvent<EventTarget>): void => {
event.preventDefault();
event.stopPropagation();
this.setState({ isCalloutVisible: true });
}
render(): React.ReactNode {
const { isCalloutVisible } = this.state;
const prompString = 'Are you sure to cancel this trial?';
return (
<div>
<div className={styles.buttonArea} ref={(menuButton): any => (this.menuButtonElement = menuButton)}>
<PrimaryButton className="detail-button-operation" onClick={this.openPromot} title="kill">{blocked}</PrimaryButton>
</div>
{isCalloutVisible ? (
<div>
<FocusTrapCallout
role="alertdialog"
className={styles.callout}
gapSpace={0}
target={this.menuButtonElement}
onDismiss={this.onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title}>Kill trial</p>
</div>
<div className={styles.inner}>
<div>
<p className={styles.subtext}>{prompString}</p>
</div>
</div>
<FocusZone>
<Stack className={styles.buttons} gap={8} horizontal>
<DefaultButton onClick={this.onDismiss}>No</DefaultButton>
<PrimaryButton onClick={this.onKill}>Yes</PrimaryButton>
</Stack>
</FocusZone>
</FocusTrapCallout>
</div>
) : null}
</div>
);
}
}
export default KillJob;
\ No newline at end of file
import * as React from 'react'; import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import { Drawer, Tabs, Row, Col, Button, Icon } from 'antd'; import {
Stack, StackItem, Panel, PrimaryButton, DefaultButton,
Pivot, PivotItem
} from 'office-ui-fabric-react';
import { infoIcon } from '../Buttons/Icon';
import { DOWNLOAD_IP } from '../../static/const'; import { DOWNLOAD_IP } from '../../static/const';
import { downFile } from '../../static/function'; import { downFile } from '../../static/function';
const { TabPane } = Tabs;
import MonacoHTML from '../public-child/MonacoEditor'; import MonacoHTML from '../public-child/MonacoEditor';
import '../../static/style/logDrawer.scss'; import '../../static/style/logDrawer.scss';
...@@ -45,22 +48,22 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> { ...@@ -45,22 +48,22 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> {
} }
} }
dispatcherHTML = (): any => { dispatcherHTML = (): React.ReactNode => {
return ( return (
<div> <div>
<span>Dispatcher Log</span> <span>Dispatcher Log</span>
<span className="refresh" onClick={this.manualRefresh}> <span className="refresh" onClick={this.manualRefresh}>
<Icon type="sync" /> {infoIcon}
</span> </span>
</div> </div>
); );
} }
nnimanagerHTML = (): any => { nnimanagerHTML = (): React.ReactNode => {
return ( return (
<div> <div>
<span>NNImanager Log</span> <span>NNImanager Log</span>
<span className="refresh" onClick={this.manualRefresh}><Icon type="sync" /></span> <span className="refresh" onClick={this.manualRefresh}>{infoIcon}</span>
</div> </div>
); );
} }
...@@ -74,7 +77,7 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> { ...@@ -74,7 +77,7 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> {
window.addEventListener('resize', this.setLogDrawerHeight); window.addEventListener('resize', this.setLogDrawerHeight);
} }
componentWillUnmount(): void { componentWillUnmount(): void{
window.clearTimeout(this.timerId); window.clearTimeout(this.timerId);
window.removeEventListener('resize', this.setLogDrawerHeight); window.removeEventListener('resize', this.setLogDrawerHeight);
} }
...@@ -82,85 +85,56 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> { ...@@ -82,85 +85,56 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> {
render(): React.ReactNode { render(): React.ReactNode {
const { closeDrawer, activeTab } = this.props; const { closeDrawer, activeTab } = this.props;
const { nniManagerLogStr, dispatcherLogStr, isLoading, logDrawerHeight } = this.state; const { nniManagerLogStr, dispatcherLogStr, isLoading, logDrawerHeight } = this.state;
return ( return (
<Row> <Stack>
<Drawer <Panel
placement="right" isOpen={true}
closable={false} hasCloseButton={false}
destroyOnClose={true} isFooterAtBottom={true}
onClose={closeDrawer}
visible={true}
width="76%"
height={logDrawerHeight}
// className="logDrawer"
> >
<div className="card-container log-tab-body"> <div className="log-tab-body">
<Tabs <Pivot
type="card" selectedKey={activeTab}
defaultActiveKey={activeTab} style={{ minHeight: 190, paddingTop: '16px' }}
style={{ height: logDrawerHeight, minHeight: 190 }}
> >
{/* <Tabs type="card" onTabClick={this.selectwhichLog} defaultActiveKey={activeTab}> */} {/* <PivotItem headerText={this.dispatcherHTML()} key="dispatcher" onLinkClick> */}
{/* <TabPane tab="Dispatcher Log" key="dispatcher"> */} <PivotItem headerText="Dispatcher Log" key="dispatcher">
<TabPane tab={this.dispatcherHTML()} key="dispatcher">
<div>
<MonacoHTML <MonacoHTML
content={dispatcherLogStr || 'Loading...'} content={dispatcherLogStr || 'Loading...'}
loading={isLoading} loading={isLoading}
height={logDrawerHeight - 104} // paddingTop[16] + tab[44] + button[32]
height={logDrawerHeight - 92}
/> />
</div> <Stack horizontal className="buttons">
<Row className="buttons"> <StackItem grow={12} className="download">
<Col span={12} className="download"> <PrimaryButton text="Download" onClick={this.downloadDispatcher} />
<Button </StackItem>
type="primary" <StackItem grow={12} className="close">
onClick={this.downloadDispatcher} <DefaultButton text="Close" onClick={closeDrawer} />
> </StackItem>
Download </Stack>
</Button> </PivotItem>
</Col> <PivotItem headerText="NNIManager Log" key="nnimanager">
<Col span={12} className="close">
<Button
type="default"
onClick={closeDrawer}
>
Close
</Button>
</Col>
</Row>
</TabPane>
<TabPane tab={this.nnimanagerHTML()} key="nnimanager">
{/* <TabPane tab="NNImanager Log" key="nnimanager"> */} {/* <TabPane tab="NNImanager Log" key="nnimanager"> */}
<div>
<MonacoHTML <MonacoHTML
content={nniManagerLogStr || 'Loading...'} content={nniManagerLogStr || 'Loading...'}
loading={isLoading} loading={isLoading}
height={logDrawerHeight - 104} height={logDrawerHeight - 92}
/> />
<Stack horizontal className="buttons">
<StackItem grow={12} className="download">
<PrimaryButton text="Download" onClick={this.downloadNNImanager} />
</StackItem>
<StackItem grow={12} className="close">
<DefaultButton text="Close" onClick={closeDrawer} />
</StackItem>
</Stack>
</PivotItem>
</Pivot>
</div> </div>
<Row className="buttons"> </Panel>
<Col span={12} className="download"> </Stack>
<Button
type="primary"
onClick={this.downloadNNImanager}
>
Download
</Button>
</Col>
<Col span={12} className="close">
<Button
type="default"
onClick={closeDrawer}
>
Close
</Button>
</Col>
</Row>
</TabPane>
</Tabs>
</div>
</Drawer>
</Row>
); );
} }
......
import * as React from 'react';
import { MessageBar, MessageBarType } from 'office-ui-fabric-react';
interface MessageInfoProps {
info: string;
typeInfo: string;
className?: string;
}
class MessageInfo extends React.Component<MessageInfoProps, {}> {
constructor(props: MessageInfoProps) {
super(props);
}
render(): React.ReactNode {
const { info, typeInfo, className } = this.props;
return (
<MessageBar
messageBarType={MessageBarType[typeInfo]}
isMultiline={false}
className={className}
>
{info}
</MessageBar>
);
}
}
export default MessageInfo;
\ No newline at end of file
.ant-modal-body{
border-radius: none;
}
.ant-modal-title {
font-size: 18px;
}
/* resubmit confirm modal style */ /* resubmit confirm modal style */
.resubmit{ .resubmit{
.title{ .title{
...@@ -29,17 +23,17 @@ ...@@ -29,17 +23,17 @@
padding: 16px 18px 16px 16px; padding: 16px 18px 16px 16px;
} }
.hyper-form{ .hyper-form{
height: 32px; margin-bottom: 10px;
margin-bottom: 8px;
.title{ .title{
font-size: 14px; font-size: 14px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 32px; line-height: 40px;
} }
.inputs{ .inputs{
height: 32px; height: 32px;
} }
input{ input{
padding-left: 5px;
height: 32px; height: 32px;
} }
} }
...@@ -69,3 +63,24 @@ ...@@ -69,3 +63,24 @@
.center{ .center{
text-align: center; text-align: center;
} }
.icon-color{
i{
color: green;
}
}
.icon-error{
i{
color: red;
}
}
.icon-color, .icon-error{
i{
margin-right: 10px;
position: relative;
top: 5px;
}
}
.ms-Dialog-subText{
color: #333;
}
\ No newline at end of file
import * as React from 'react';
import axios from 'axios';
import { WEBUIDOC, MANAGER_IP } from '../static/const';
import {
Stack, initializeIcons, StackItem, CommandBarButton,
IContextualMenuProps, IStackTokens, IStackStyles
} from 'office-ui-fabric-react';
import LogDrawer from './Modals/LogDrawer';
import ExperimentDrawer from './Modals/ExperimentDrawer';
import {
downLoadIcon, infoIconAbout,
timeIcon, disableUpdates, requency, closeTimer
} from './Buttons/Icon';
import { OVERVIEWTABS, DETAILTABS, NNILOGO } from './stateless-component/NNItabs';
import '../static/style/nav/nav.scss';
import '../static/style/icon.scss';
initializeIcons();
const stackTokens: IStackTokens = {
childrenGap: 15
};
const stackStyle: IStackStyles = {
root: {
minWidth: 400, height: 56, display: 'flex', verticalAlign: 'center'
}
};
interface NavState {
version: string;
menuVisible: boolean;
navBarVisible: boolean;
isdisabledFresh: boolean;
isvisibleLogDrawer: boolean;
isvisibleExperimentDrawer: boolean;
refreshText: string;
refreshFrequency: number | string;
}
interface NavProps {
changeInterval: (value: number) => void;
refreshFunction: () => void;
}
class NavCon extends React.Component<NavProps, NavState> {
constructor(props: NavProps) {
super(props);
this.state = {
version: '',
menuVisible: false,
navBarVisible: false,
isdisabledFresh: false,
isvisibleLogDrawer: false, // download button (nnimanager·dispatcher) click -> drawer
isvisibleExperimentDrawer: false,
refreshText: 'Auto refresh',
refreshFrequency: 10
};
}
// to see & download experiment parameters
showExpcontent = (): void => {
this.setState({ isvisibleExperimentDrawer: true });
}
// to see & download dispatcher | nnimanager log
showDispatcherLog = (): void => {
this.setState({ isvisibleLogDrawer: true });
}
// close log drawer (nnimanager.dispatcher)
closeLogDrawer = (): void => {
this.setState({ isvisibleLogDrawer: false });
}
// close download experiment parameters drawer
closeExpDrawer = (): void => {
this.setState({ isvisibleExperimentDrawer: false });
}
getNNIversion = (): void => {
axios(`${MANAGER_IP}/version`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
this.setState({ version: res.data });
}
});
}
openGithub = (): void => {
const { version } = this.state;
const feed = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
window.open(feed);
}
openDocs = (): void => {
window.open(WEBUIDOC);
}
openGithubNNI = (): void => {
const {version} = this.state;
const nniLink = `https://github.com/Microsoft/nni/tree/${version}`;
window.open(nniLink);
}
getInterval = (num: number): void => {
this.props.changeInterval(num); // notice parent component
this.setState(() => ({
refreshFrequency: num === 0 ? '' : num,
refreshText: num === 0 ? 'Disable auto' : 'Auto refresh'
}));
}
componentDidMount(): void {
this.getNNIversion();
}
render(): React.ReactNode {
const { isvisibleLogDrawer, isvisibleExperimentDrawer, version,
refreshText, refreshFrequency } = this.state;
const aboutProps: IContextualMenuProps = {
items: [
{
key: 'feedback',
text: 'Feedback',
iconProps: { iconName: 'OfficeChat' },
onClick: this.openGithub
},
{
key: 'help',
text: 'Document',
iconProps: { iconName: 'TextDocument' },
onClick: this.openDocs
},
{
key: 'version',
text: `Version ${version}`,
iconProps: { iconName: 'VerifiedBrand' },
onClick: this.openGithubNNI
}
]
};
return (
<Stack horizontal className="nav">
<StackItem grow={30} styles={{ root: { minWidth: 300, display: 'flex', verticalAlign: 'center' } }}>
<span className="desktop-logo">{NNILOGO}</span>
<span className="left-right-margin">{OVERVIEWTABS}</span>
<span>{DETAILTABS}</span>
</StackItem>
<StackItem grow={70} className="navOptions">
<Stack horizontal horizontalAlign="end" tokens={stackTokens} styles={stackStyle}>
{/* refresh button danyi*/}
{/* TODO: fix bug */}
{/* <CommandBarButton
iconProps={{ iconName: 'sync' }}
text="Refresh"
onClick={this.props.refreshFunction}
/> */}
<div className="nav-refresh">
<CommandBarButton
iconProps={refreshFrequency === '' ? disableUpdates : timeIcon}
text={refreshText}
menuProps={this.refreshProps}
/>
<div className="nav-refresh-num">{refreshFrequency}</div>
</div>
<CommandBarButton
iconProps={downLoadIcon}
text="Download"
menuProps={this.menuProps}
/>
<CommandBarButton
iconProps={infoIconAbout}
text="about"
menuProps={aboutProps}
/>
</Stack>
</StackItem>
{/* the drawer for dispatcher & nnimanager log message */}
{isvisibleLogDrawer && <LogDrawer closeDrawer={this.closeLogDrawer} />}
<ExperimentDrawer isVisble={isvisibleExperimentDrawer} closeExpDrawer={this.closeExpDrawer} />
</Stack>
);
}
// view and download experiment [log & experiment result]
private menuProps: IContextualMenuProps = {
items: [
{
key: 'experiment',
text: 'Experiment Summary',
iconProps: { iconName: 'ShowResults' },
onClick: this.showExpcontent
},
{
key: 'logfiles',
text: 'Logfiles',
iconProps: { iconName: 'FilePDB' },
onClick: this.showDispatcherLog
}
],
directionalHintFixed: true
};
private refreshProps: IContextualMenuProps = {
items: [
{
key: 'disableRefresh',
text: 'Disable auto refresh',
iconProps: closeTimer,
onClick: this.getInterval.bind(this, 0)
},
{
key: 'refresh10',
text: 'Refresh every 10s',
iconProps: requency,
onClick: this.getInterval.bind(this, 10)
},
{
key: 'refresh20',
text: 'Refresh every 20s',
iconProps: requency,
onClick: this.getInterval.bind(this, 20)
},
{
key: 'refresh30',
text: 'Refresh every 30s',
iconProps: requency,
onClick: this.getInterval.bind(this, 30)
},
{
key: 'refresh60',
text: 'Refresh every 1min',
iconProps: requency,
onClick: this.getInterval.bind(this, 60)
},
]
};
}
export default NavCon;
import * as React from 'react'; import * as React from 'react';
import { Row, Col } from 'antd'; import { Stack, IStackTokens } from 'office-ui-fabric-react';
import { EXPERIMENT, TRIALS } from '../static/datamodel'; import { EXPERIMENT, TRIALS } from '../static/datamodel';
import { Trial } from '../static/model/trial'; import { Trial } from '../static/model/trial';
import SuccessTable from './overview/SuccessTable';
import Title1 from './overview/Title1'; import Title1 from './overview/Title1';
import SuccessTable from './overview/SuccessTable';
import Progressed from './overview/Progress'; import Progressed from './overview/Progress';
import Accuracy from './overview/Accuracy'; import Accuracy from './overview/Accuracy';
import SearchSpace from './overview/SearchSpace'; import SearchSpace from './overview/SearchSpace';
import BasicInfo from './overview/BasicInfo'; import BasicInfo from './overview/BasicInfo';
import TrialInfo from './overview/TrialProfile'; import TrialInfo from './overview/TrialProfile';
import '../static/style/overview.scss';
require('../static/style/overview.scss'); import '../static/style/logPath.scss';
require('../static/style/logPath.scss');
require('../static/style/accuracy.css');
require('../static/style/table.scss');
require('../static/style/overviewTitle.scss');
interface OverviewProps { interface OverviewProps {
experimentUpdateBroadcast: number; experimentUpdateBroadcast: number;
...@@ -55,9 +51,7 @@ class Overview extends React.Component<OverviewProps, OverviewState> { ...@@ -55,9 +51,7 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
render(): React.ReactNode { render(): React.ReactNode {
const { trialConcurrency } = this.state; const { trialConcurrency } = this.state;
const { experimentUpdateBroadcast, metricGraphMode } = this.props; const { experimentUpdateBroadcast, metricGraphMode } = this.props;
const searchSpace = this.convertSearchSpace(); const searchSpace = this.convertSearchSpace();
const bestTrials = this.findBestTrials(); const bestTrials = this.findBestTrials();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const bestAccuracy = bestTrials.length > 0 ? bestTrials[0].accuracy! : NaN; const bestAccuracy = bestTrials.length > 0 ? bestTrials[0].accuracy! : NaN;
...@@ -67,16 +61,20 @@ class Overview extends React.Component<OverviewProps, OverviewState> { ...@@ -67,16 +61,20 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
const titleMaxbgcolor = (metricGraphMode === 'max' ? '#999' : '#b3b3b3'); const titleMaxbgcolor = (metricGraphMode === 'max' ? '#999' : '#b3b3b3');
const titleMinbgcolor = (metricGraphMode === 'min' ? '#999' : '#b3b3b3'); const titleMinbgcolor = (metricGraphMode === 'min' ? '#999' : '#b3b3b3');
const stackTokens: IStackTokens = {
childrenGap: 30,
};
return ( return (
<div className="overview"> <div className="overview">
{/* status and experiment block */} {/* status and experiment block */}
<Row> <Stack>
<Title1 text="Experiment" icon="11.png" /> <Title1 text="Experiment" icon="11.png" />
<BasicInfo experimentUpdateBroadcast={experimentUpdateBroadcast} /> <BasicInfo experimentUpdateBroadcast={experimentUpdateBroadcast} />
</Row> </Stack>
<Row className="overMessage">
{/* status graph */} <Stack horizontal className="overMessage">
<Col span={9} className="prograph overviewBoder cc"> {/* status block */}
<Stack.Item styles={{ root: { maxWidth: 440 } }} className="prograph overviewBoder cc">
<Title1 text="Status" icon="5.png" /> <Title1 text="Status" icon="5.png" />
<Progressed <Progressed
bestAccuracy={bestAccuracy} bestAccuracy={bestAccuracy}
...@@ -84,17 +82,17 @@ class Overview extends React.Component<OverviewProps, OverviewState> { ...@@ -84,17 +82,17 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
changeConcurrency={this.changeConcurrency} changeConcurrency={this.changeConcurrency}
experimentUpdateBroadcast={experimentUpdateBroadcast} experimentUpdateBroadcast={experimentUpdateBroadcast}
/> />
</Col> </Stack.Item>
{/* experiment parameters search space tuner assessor... */} {/* experiment parameters search space tuner assessor... */}
<Col span={7} className="overviewBoder cc"> <Stack.Item styles={{root: {width: 400}}} className="overviewBoder">
<Title1 text="Search space" icon="10.png" /> <Title1 text="Search space" icon="10.png" />
<Row className="experiment"> <Stack className="experiment">
<SearchSpace searchSpace={searchSpace} /> <SearchSpace searchSpace={searchSpace} />
</Row> </Stack>
</Col> </Stack.Item>
<Col span={8} className="overviewBoder cc"> <Stack.Item styles={{root: {width: 400}}}>
<Title1 text="Profile" icon="4.png" /> <Title1 text="Config" icon="4.png" />
<Row className="experiment"> <Stack className="experiment">
{/* the scroll bar all the trial profile in the searchSpace div*/} {/* the scroll bar all the trial profile in the searchSpace div*/}
<div className="experiment searchSpace"> <div className="experiment searchSpace">
<TrialInfo <TrialInfo
...@@ -102,44 +100,38 @@ class Overview extends React.Component<OverviewProps, OverviewState> { ...@@ -102,44 +100,38 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
concurrency={trialConcurrency} concurrency={trialConcurrency}
/> />
</div> </div>
</Row> </Stack>
</Col> </Stack.Item>
</Row> </Stack>
<Row className="overGraph">
<Row className="top10bg"> <Stack>
<Col span={4} className="top10Title"> <Stack horizontal className="top10bg">
<Title1 text="Top10 trials" icon="7.png" /> <div
</Col>
<Col
span={2}
className="title" className="title"
onClick={this.clickMaxTop} onClick={this.clickMaxTop}
> >
<Title1 text="Maximal" icon="max.png" bgcolor={titleMaxbgcolor} /> <Title1 text="Top10 Maximal trials" icon="max.png" bgcolor={titleMaxbgcolor} />
</Col> </div>
<Col <div
span={2}
className="title minTitle" className="title minTitle"
onClick={this.clickMinTop} onClick={this.clickMinTop}
> >
<Title1 text="Minimal" icon="min.png" bgcolor={titleMinbgcolor} /> <Title1 text="Top10 Minimal trials" icon="min.png" bgcolor={titleMinbgcolor} />
</Col> </div>
</Row> </Stack>
<Row> <Stack horizontal tokens={stackTokens}>
<Col span={8} className="overviewBoder"> <div style={{ width: '40%'}}>
<Row className="accuracy">
<Accuracy <Accuracy
accuracyData={accuracyGraphData} accuracyData={accuracyGraphData}
accNodata={noDataMessage} accNodata={noDataMessage}
height={324} height={404}
/> />
</Row> </div>
</Col> <div style={{ width: '60%'}}>
<Col span={16} id="succeTable"> <SuccessTable trialIds={bestTrials.map(trial => trial.info.id)} />
<SuccessTable trialIds={bestTrials.map(trial => trial.info.id)}/> </div>
</Col> </Stack>
</Row> </Stack>
</Row>
</div> </div>
); );
} }
......
import * as React from 'react';
import { Link } from 'react-router';
import axios from 'axios';
import { MANAGER_IP } from '../static/const';
import MediaQuery from 'react-responsive';
import { Row, Col, Menu, Dropdown, Icon, Select, Button, Form } from 'antd';
import { FormComponentProps } from 'antd/lib/form';
import { OVERVIEWTABS, DETAILTABS, NNILOGO } from './stateless-component/NNItabs';
const { SubMenu } = Menu;
const { Option } = Select;
const FormItem = Form.Item;
import LogDrawer from './Modal/LogDrawer';
import ExperimentDrawer from './Modal/ExperimentDrawer';
import '../static/style/slideBar.scss';
import '../static/style/button.scss';
interface SliderState {
version: string;
menuVisible: boolean;
navBarVisible: boolean;
isdisabledFresh: boolean;
isvisibleLogDrawer: boolean;
isvisibleExperimentDrawer: boolean;
activeKey: string;
}
interface SliderProps extends FormComponentProps {
changeInterval: (value: number) => void;
}
interface EventPer {
key: string;
}
class SlideBar extends React.Component<SliderProps, SliderState> {
public divMenu: HTMLDivElement | null;
public selectHTML: Select | null;
constructor(props: SliderProps) {
super(props);
this.state = {
version: '',
menuVisible: false,
navBarVisible: false,
isdisabledFresh: false,
isvisibleLogDrawer: false, // download button (nnimanager·dispatcher) click -> drawer
isvisibleExperimentDrawer: false,
activeKey: 'dispatcher'
};
}
getNNIversion = (): void => {
axios(`${MANAGER_IP}/version`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
this.setState({ version: res.data });
}
});
}
handleMenuClick = (e: EventPer): void => {
this.setState({ menuVisible: false });
switch (e.key) {
// to see & download experiment parameters
case '1':
this.setState({ isvisibleExperimentDrawer: true });
break;
// to see & download nnimanager log
case '2':
this.setState({ activeKey: 'nnimanager', isvisibleLogDrawer: true });
break;
// to see & download dispatcher log
case '3':
this.setState({ isvisibleLogDrawer: true, activeKey: 'dispatcher' });
break;
case 'close':
case '10':
case '20':
case '30':
case '60':
this.getInterval(e.key);
break;
default:
}
}
handleVisibleChange = (flag: boolean): void => {
this.setState({ menuVisible: flag });
}
getInterval = (value: string): void => {
if (value === 'close') {
this.props.changeInterval(0);
} else {
this.props.changeInterval(parseInt(value, 10));
}
}
menu = (): any => {
return (
<Menu onClick={this.handleMenuClick}>
<Menu.Item key="1">Experiment Parameters</Menu.Item>
<Menu.Item key="2">NNImanager Logfile</Menu.Item>
<Menu.Item key="3">Dispatcher Logfile</Menu.Item>
</Menu>
);
}
// nav bar
navigationBar = (): any => {
const { version } = this.state;
const feedBackLink = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
return (
<Menu onClick={this.handleMenuClick} className="menu-list" style={{ width: 216 }}>
{/* <Menu onClick={this.handleMenuClick} className="menu-list" style={{width: window.innerWidth}}> */}
<Menu.Item key="feedback">
<a href={feedBackLink} rel="noopener noreferrer" target="_blank">Feedback</a>
</Menu.Item>
<Menu.Item key="version">Version: {version}</Menu.Item>
<SubMenu
key="download"
onChange={this.handleVisibleChange}
title={
<span>
<span>View</span>
</span>
}
>
<Menu.Item key="1">Experiment Parameters</Menu.Item>
<Menu.Item key="2">NNImanager Logfile</Menu.Item>
<Menu.Item key="3">Dispatcher Logfile</Menu.Item>
</SubMenu>
</Menu>
);
}
mobileTabs = (): any => {
return (
// <Menu className="menuModal" style={{width: 880, position: 'fixed', left: 0, top: 56}}>
<Menu className="menuModal" style={{ padding: '0 10px' }}>
<Menu.Item key="overview"><Link to={'/oview'}>Overview</Link></Menu.Item>
<Menu.Item key="detail"><Link to={'/detail'}>Trials detail</Link></Menu.Item>
</Menu>
);
}
refreshInterval = (): any => {
const {
form: { getFieldDecorator },
// form: { getFieldDecorator, getFieldValue },
} = this.props;
return (
<Form>
<FormItem style={{ marginBottom: 0 }}>
{getFieldDecorator('interval', {
initialValue: 'Refresh every 10s',
})(
<Select onSelect={this.getInterval}>
<Option value="close">Disable Auto Refresh</Option>
<Option value="10">Refresh every 10s</Option>
<Option value="20">Refresh every 20s</Option>
<Option value="30">Refresh every 30s</Option>
<Option value="60">Refresh every 1min</Option>
</Select>,
)}
</FormItem>
</Form>
);
}
select = (): any => {
const { isdisabledFresh } = this.state;
return (
<div className="interval">
{this.refreshInterval()}
<Button
className="fresh"
onClick={this.fresh}
type="ghost"
disabled={isdisabledFresh}
>
<Icon type="sync" /><span>Refresh</span>
</Button>
</div>
);
}
fresh = (event: React.SyntheticEvent<EventTarget>): void => {
event.preventDefault();
event.stopPropagation();
this.setState({ isdisabledFresh: true }, () => {
setTimeout(() => { this.setState({ isdisabledFresh: false }); }, 1000);
});
}
desktopHTML = (): any => {
const { version, menuVisible } = this.state;
const feed = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
return (
<Row className="nav">
<Col span={8}>
<span className="desktop-logo">{NNILOGO}</span>
<span className="left-right-margin">{OVERVIEWTABS}</span>
<span>{DETAILTABS}</span>
</Col>
<Col span={16} className="desktop-right">
<span>
<Button
className="fresh"
type="ghost"
>
<a target="_blank" rel="noopener noreferrer" href="https://nni.readthedocs.io/en/latest/Tutorial/WebUI.html">
<img
src={require('../static/img/icon/ques.png')}
alt="question"
className="question"
/>
<span>Help</span>
</a>
</Button>
</span>
<span>{this.select()}</span>
<span>
<Dropdown
className="dropdown"
overlay={this.menu()}
onVisibleChange={this.handleVisibleChange}
visible={menuVisible}
trigger={['click']}
>
<a className="ant-dropdown-link" href="#">
<Icon type="download" className="down-icon" />
<span>View</span>
{
menuVisible
?
<Icon type="up" className="margin-icon" />
:
<Icon type="down" className="margin-icon" />
}
</a>
</Dropdown>
</span>
<span className="feedback">
<a href={feed} target="_blank" rel="noopener noreferrer">
<img
src={require('../static/img/icon/issue.png')}
alt="NNI github issue"
/>
Feedback
</a>
</span>
<span className="version">Version: {version}</span>
</Col>
</Row>
);
}
tabeltHTML = (): any => {
return (
<Row className="nav">
<Col className="tabelt-left" span={14}>
<span>
<Dropdown overlay={this.navigationBar()} trigger={['click']}>
<Icon type="unordered-list" className="more" />
</Dropdown>
</span>
<span className="left-right-margin tabelt-img">{NNILOGO}</span>
<span>{OVERVIEWTABS}</span>
<span className="left-margin">{DETAILTABS}</span>
</Col>
<Col className="tabelt-right" span={10}>
{this.select()}
</Col>
</Row>
);
}
mobileHTML = (): any => {
const { isdisabledFresh } = this.state;
return (
<Row className="nav">
<Col className="left" span={8}>
<span>
<Dropdown className="more-mobile" overlay={this.navigationBar()} trigger={['click']}>
<Icon type="unordered-list" className="more" />
</Dropdown>
</span>
<span>
<Dropdown overlay={this.mobileTabs()} trigger={['click']}>
<a className="ant-dropdown-link" href="#">
<span>NNI <Icon type="down" /></span>
</a>
</Dropdown>
</span>
</Col>
<Col className="center" span={8}>
<img
src={require('../static/img/logo2.png')}
alt="NNI logo"
/>
</Col>
{/* the class interval have other style ! */}
<Col className="right interval" span={8}>
<Button
className="fresh"
onClick={this.fresh}
type="ghost"
disabled={isdisabledFresh}
>
<Icon type="sync" /><span>Refresh</span>
</Button>
</Col>
</Row>
);
}
// close log drawer (nnimanager.dispatcher)
closeLogDrawer = (): void => {
this.setState({ isvisibleLogDrawer: false, activeKey: '' });
}
// close download experiment parameters drawer
closeExpDrawer = (): void => {
this.setState({ isvisibleExperimentDrawer: false });
}
componentDidMount(): void {
this.getNNIversion();
}
render(): React.ReactNode {
const mobile = (<MediaQuery maxWidth={884}>{this.mobileHTML()}</MediaQuery>);
const tablet = (<MediaQuery minWidth={885} maxWidth={1281}>{this.tabeltHTML()}</MediaQuery>);
const desktop = (<MediaQuery minWidth={1282}>{this.desktopHTML()}</MediaQuery>);
const { isvisibleLogDrawer, activeKey, isvisibleExperimentDrawer } = this.state;
return (
<div>
{mobile}
{tablet}
{desktop}
{/* the drawer for dispatcher & nnimanager log message */}
{isvisibleLogDrawer ? (
<LogDrawer
closeDrawer={this.closeLogDrawer}
activeTab={activeKey}
/>
) : null}
<ExperimentDrawer
isVisble={isvisibleExperimentDrawer}
closeExpDrawer={this.closeExpDrawer}
/>
</div>
);
}
}
export default Form.create<FormComponentProps>()(SlideBar);
import * as React from 'react'; import * as React from 'react';
import { Row, Col, Tabs, Select, Button, Icon } from 'antd'; import {
const Option = Select.Option; Stack, StackItem, Pivot, PivotItem, Dropdown, IDropdownOption, DefaultButton
} from 'office-ui-fabric-react';
import { EXPERIMENT, TRIALS } from '../static/datamodel'; import { EXPERIMENT, TRIALS } from '../static/datamodel';
import { Trial } from '../static/model/trial'; import { Trial } from '../static/model/trial';
import { tableListIcon } from './Buttons/Icon';
import DefaultPoint from './trial-detail/DefaultMetricPoint'; import DefaultPoint from './trial-detail/DefaultMetricPoint';
import Duration from './trial-detail/Duration'; import Duration from './trial-detail/Duration';
import Title1 from './overview/Title1';
import Para from './trial-detail/Para'; import Para from './trial-detail/Para';
import Intermediate from './trial-detail/Intermediate'; import Intermediate from './trial-detail/Intermediate';
import TableList from './trial-detail/TableList'; import TableList from './trial-detail/TableList';
const TabPane = Tabs.TabPane;
import '../static/style/trialsDetail.scss'; import '../static/style/trialsDetail.scss';
import '../static/style/search.scss'; import '../static/style/search.scss';
...@@ -21,8 +21,8 @@ interface TrialDetailState { ...@@ -21,8 +21,8 @@ interface TrialDetailState {
} }
interface TrialsDetailProps { interface TrialsDetailProps {
columnList: Array<string>; columnList: string[];
changeColumn: (val: Array<string>) => void; changeColumn: (val: string[]) => void;
experimentUpdateBroacast: number; experimentUpdateBroacast: number;
trialsUpdateBroadcast: number; trialsUpdateBroadcast: number;
} }
...@@ -32,44 +32,25 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState> ...@@ -32,44 +32,25 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
public interAccuracy = 0; public interAccuracy = 0;
public interAllTableList = 2; public interAllTableList = 2;
public tableList: TableList | null; public tableList!: TableList | null;
public searchInput: HTMLInputElement | null; public searchInput!: HTMLInputElement | null;
private titleOfacc = (
<Title1 text="Default metric" icon="3.png" />
);
private titleOfhyper = (
<Title1 text="Hyper-parameter" icon="1.png" />
);
private titleOfDuration = (
<Title1 text="Trial duration" icon="2.png" />
);
private titleOfIntermediate = (
<div className="panelTitle">
<Icon type="line-chart" />
<span>Intermediate result</span>
</div>
);
constructor(props: TrialsDetailProps) { constructor(props: TrialsDetailProps) {
super(props); super(props);
this.state = { this.state = {
tablePageSize: 20, tablePageSize: 20,
whichGraph: '1', whichGraph: '1',
searchType: 'id', searchType: 'Id',
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-function-return-type
searchFilter: trial => true searchFilter: trial => true
}; };
} }
// search a trial by trial No. & trial id // search a trial by trial No. | trial id | Parameters | Status
searchTrial = (event: React.ChangeEvent<HTMLInputElement>): void => { searchTrial = (event: React.ChangeEvent<HTMLInputElement>): void => {
const targetValue = event.target.value; const targetValue = event.target.value;
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
let filter = (trial: Trial) => true; let filter = (trial: Trial): boolean => true;
if (!targetValue.trim()) { if (!targetValue.trim()) {
this.setState({ searchFilter: filter }); this.setState({ searchFilter: filter });
return; return;
...@@ -81,7 +62,7 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState> ...@@ -81,7 +62,7 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
case 'Trial No.': case 'Trial No.':
filter = (trial): boolean => trial.info.sequenceId.toString() === targetValue; filter = (trial): boolean => trial.info.sequenceId.toString() === targetValue;
break; break;
case 'status': case 'Status':
filter = (trial): boolean => trial.info.status.toUpperCase().includes(targetValue.toUpperCase()); filter = (trial): boolean => trial.info.status.toUpperCase().includes(targetValue.toUpperCase());
break; break;
case 'parameters': case 'parameters':
...@@ -94,112 +75,119 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState> ...@@ -94,112 +75,119 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
this.setState({ searchFilter: filter }); this.setState({ searchFilter: filter });
} }
handleTablePageSizeSelect = (value: string): void => { handleTablePageSizeSelect = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void => {
this.setState({ tablePageSize: value === 'all' ? -1 : parseInt(value, 10) }); if (item !== undefined) {
this.setState({ tablePageSize: item.text === 'all' ? -1 : parseInt(item.text, 10) });
}
} }
handleWhichTabs = (activeKey: string): void => { handleWhichTabs = (activeKey: string): void => {
this.setState({ whichGraph: activeKey }); this.setState({ whichGraph: activeKey });
} }
updateSearchFilterType = (value: string): void => { updateSearchFilterType = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void => {
// clear input value and re-render table // clear input value and re-render table
if (item !== undefined) {
if (this.searchInput !== null) { if (this.searchInput !== null) {
this.searchInput.value = ''; this.searchInput.value = '';
} }
this.setState({ searchType: value }); this.setState(() => ({ searchType: item.text }));
}
} }
render(): React.ReactNode { render(): React.ReactNode {
const { tablePageSize, whichGraph } = this.state; const { tablePageSize, whichGraph, searchType } = this.state;
const { columnList, changeColumn } = this.props; const { columnList, changeColumn } = this.props;
const source = TRIALS.filter(this.state.searchFilter); const source = TRIALS.filter(this.state.searchFilter);
const trialIds = TRIALS.filter(this.state.searchFilter).map(trial => trial.id); const trialIds = TRIALS.filter(this.state.searchFilter).map(trial => trial.id);
const searchOptions = [
{ key: 'Id', text: 'Id' },
{ key: 'Trial No.', text: 'Trial No.' },
{ key: 'Status', text: 'Status' },
{ key: 'Parameters', text: 'Parameters' },
];
return ( return (
<div> <div>
<div className="trial" id="tabsty"> <div className="trial" id="tabsty">
<Tabs type="card" onChange={this.handleWhichTabs}> <Pivot defaultSelectedKey={"0"} className="detial-title">
<TabPane tab={this.titleOfacc} key="1"> {/* <PivotItem tab={this.titleOfacc} key="1"> doesn't work*/}
<Row className="graph"> <PivotItem headerText="Default metric" itemIcon="HomeGroup" key="1">
<Stack className="graph">
<DefaultPoint <DefaultPoint
trialIds={trialIds} trialIds={trialIds}
visible={whichGraph === '1'} visible={whichGraph === '1'}
trialsUpdateBroadcast={this.props.trialsUpdateBroadcast} trialsUpdateBroadcast={this.props.trialsUpdateBroadcast}
/> />
</Row> </Stack>
</TabPane> </PivotItem>
<TabPane tab={this.titleOfhyper} key="2"> {/* <PivotItem tab={this.titleOfhyper} key="2"> */}
<Row className="graph"> <PivotItem headerText="Hyper-parameter" itemIcon="Equalizer" key="2">
<Stack className="graph">
<Para <Para
dataSource={source} dataSource={source}
expSearchSpace={JSON.stringify(EXPERIMENT.searchSpace)} expSearchSpace={JSON.stringify(EXPERIMENT.searchSpace)}
whichGraph={whichGraph} whichGraph={whichGraph}
/> />
</Row> </Stack>
</TabPane> </PivotItem>
<TabPane tab={this.titleOfDuration} key="3"> {/* <PivotItem tab={this.titleOfDuration} key="3"> */}
<PivotItem headerText="Duration" itemIcon="BarChartHorizontal" key="3">
<Duration source={source} whichGraph={whichGraph} /> <Duration source={source} whichGraph={whichGraph} />
</TabPane> </PivotItem>
<TabPane tab={this.titleOfIntermediate} key="4"> {/* <PivotItem tab={this.titleOfIntermediate} key="4"> */}
<PivotItem headerText="Intermediate result" itemIcon="StackedLineChart" key="4">
{/* *why this graph has small footprint? */}
<Intermediate source={source} whichGraph={whichGraph} /> <Intermediate source={source} whichGraph={whichGraph} />
</TabPane> </PivotItem>
</Tabs> </Pivot>
</div> </div>
{/* trial table list */} {/* trial table list */}
<Title1 text="Trial jobs" icon="6.png" /> <Stack horizontal className="panelTitle">
<Row className="allList"> <span style={{ marginRight: 12 }}>{tableListIcon}</span>
<Col span={10}> <span>Trial jobs</span>
<span>Show</span> </Stack>
<Select <Stack horizontal className="allList">
className="entry" <StackItem grow={50}>
onSelect={this.handleTablePageSizeSelect} <DefaultButton
defaultValue="20" text="Compare"
> className="allList-compare"
<Option value="20">20</Option>
<Option value="50">50</Option>
<Option value="100">100</Option>
<Option value="all">All</Option>
</Select>
<span>entries</span>
</Col>
<Col span={14} className="right">
<Button
className="common"
onClick={(): void => { if (this.tableList) { this.tableList.addColumn(); }}}
>
Add column
</Button>
<Button
className="mediateBtn common"
// use child-component tableList's function, the function is in child-component. // use child-component tableList's function, the function is in child-component.
onClick={(): void => { if (this.tableList) { this.tableList.compareBtn(); }}} onClick={(): void => { if (this.tableList) { this.tableList.compareBtn(); } }}
> />
Compare </StackItem>
</Button> <StackItem grow={50}>
<Select defaultValue="id" className="filter" onSelect={this.updateSearchFilterType}> <Stack horizontal horizontalAlign="end" className="allList">
<Option value="id">Id</Option> <DefaultButton
<Option value="Trial No.">Trial No.</Option> className="allList-button-gap"
<Option value="status">Status</Option> text="Add column"
<Option value="parameters">Parameters</Option> onClick={(): void => { if (this.tableList) { this.tableList.addColumn(); } }}
</Select> />
<Dropdown
selectedKey={searchType}
options={searchOptions}
onChange={this.updateSearchFilterType}
styles={{ root: { width: 150 } }}
/>
<input <input
type="text" type="text"
className="search-input" className="allList-search-input"
placeholder={`Search by ${this.state.searchType}`} placeholder={`Search by ${this.state.searchType}`}
onChange={this.searchTrial} onChange={this.searchTrial}
style={{ width: 230 }} style={{ width: 230 }}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type ref={(text): any => (this.searchInput) = text}
ref={text => (this.searchInput) = text}
/> />
</Col> </Stack>
</Row>
</StackItem>
</Stack>
<TableList <TableList
pageSize={tablePageSize} pageSize={tablePageSize}
tableSource={source.map(trial => trial.tableRecord)} tableSource={source.map(trial => trial.tableRecord)}
columnList={columnList} columnList={columnList}
changeColumn={changeColumn} changeColumn={changeColumn}
trialsUpdateBroadcast={this.props.trialsUpdateBroadcast} trialsUpdateBroadcast={this.props.trialsUpdateBroadcast}
ref={(tabList) => this.tableList = tabList} // eslint-disable-line @typescript-eslint/explicit-function-return-type // TODO: change any to specific type
ref={(tabList): any => this.tableList = tabList}
/> />
</div> </div>
); );
......
import * as React from 'react'; import * as React from 'react';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
const echarts = require('echarts/lib/echarts'); import echarts from 'echarts/lib/echarts';
echarts.registerTheme('my_theme', { echarts.registerTheme('my_theme', {
color: '#3c8dbc' color: '#3c8dbc'
}); });
require('echarts/lib/chart/scatter'); import 'echarts/lib/chart/scatter';
require('echarts/lib/component/tooltip'); import 'echarts/lib/component/tooltip';
require('echarts/lib/component/title'); import 'echarts/lib/component/title';
interface AccuracyProps { interface AccuracyProps {
accuracyData: object; accuracyData: object;
...@@ -28,7 +28,6 @@ class Accuracy extends React.Component<AccuracyProps, {}> { ...@@ -28,7 +28,6 @@ class Accuracy extends React.Component<AccuracyProps, {}> {
<ReactEcharts <ReactEcharts
option={accuracyData} option={accuracyData}
style={{ style={{
width: '90%',
height: height, height: height,
margin: '0 auto', margin: '0 auto',
}} }}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment