Unverified Commit c2e69672 authored by Lijiaoa's avatar Lijiaoa Committed by GitHub
Browse files

[webui v1.9 bug bash] fix bugs in v1.9 (#2982)

parent f1105409
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
.ms-Callout-main { .ms-Callout-main {
p { p {
font-weight: 500; font-weight: 500;
color: #333; color: #fff;
} }
} }
......
...@@ -12,6 +12,7 @@ interface AppState { ...@@ -12,6 +12,7 @@ interface AppState {
columnList: string[]; columnList: string[];
experimentUpdateBroadcast: number; experimentUpdateBroadcast: number;
trialsUpdateBroadcast: number; trialsUpdateBroadcast: number;
maxDurationUnit: string;
metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed
isillegalFinal: boolean; isillegalFinal: boolean;
expWarningMessage: string; expWarningMessage: string;
...@@ -26,11 +27,14 @@ export const AppContext = React.createContext({ ...@@ -26,11 +27,14 @@ export const AppContext = React.createContext({
trialsUpdateBroadcast: 0, trialsUpdateBroadcast: 0,
metricGraphMode: 'max', metricGraphMode: 'max',
bestTrialEntries: '10', bestTrialEntries: '10',
maxDurationUnit: 'm',
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
changeColumn: (val: string[]) => {}, changeColumn: (val: string[]) => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
changeMetricGraphMode: (val: 'max' | 'min') => {}, changeMetricGraphMode: (val: 'max' | 'min') => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
changeMaxDurationUnit: (val: string) => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
changeEntries: (val: string) => {}, changeEntries: (val: string) => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
updateOverviewPage: () => {} updateOverviewPage: () => {}
...@@ -47,6 +51,7 @@ class App extends React.Component<{}, AppState> { ...@@ -47,6 +51,7 @@ class App extends React.Component<{}, AppState> {
experimentUpdateBroadcast: 0, experimentUpdateBroadcast: 0,
trialsUpdateBroadcast: 0, trialsUpdateBroadcast: 0,
metricGraphMode: 'max', metricGraphMode: 'max',
maxDurationUnit: 'm',
isillegalFinal: false, isillegalFinal: false,
expWarningMessage: '', expWarningMessage: '',
bestTrialEntries: '10', bestTrialEntries: '10',
...@@ -91,6 +96,11 @@ class App extends React.Component<{}, AppState> { ...@@ -91,6 +96,11 @@ class App extends React.Component<{}, AppState> {
this.setState({ bestTrialEntries: entries }); this.setState({ bestTrialEntries: entries });
}; };
// overview max duration unit
changeMaxDurationUnit = (unit: string): void => {
this.setState({ maxDurationUnit: unit });
};
updateOverviewPage = (): void => { updateOverviewPage = (): void => {
this.setState(state => ({ this.setState(state => ({
experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1
...@@ -114,7 +124,8 @@ class App extends React.Component<{}, AppState> { ...@@ -114,7 +124,8 @@ class App extends React.Component<{}, AppState> {
metricGraphMode, metricGraphMode,
isillegalFinal, isillegalFinal,
expWarningMessage, expWarningMessage,
bestTrialEntries bestTrialEntries,
maxDurationUnit
} = this.state; } = 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
...@@ -137,7 +148,24 @@ class App extends React.Component<{}, AppState> { ...@@ -137,7 +148,24 @@ class App extends React.Component<{}, AppState> {
<Stack className='contentBox'> <Stack className='contentBox'>
<Stack className='content'> <Stack className='content'>
{/* search space & config */} {/* search space & config */}
<TrialConfigButton /> <AppContext.Provider
value={{
interval,
columnList,
changeColumn: this.changeColumn,
experimentUpdateBroadcast,
trialsUpdateBroadcast,
metricGraphMode,
maxDurationUnit,
changeMaxDurationUnit: this.changeMaxDurationUnit,
changeMetricGraphMode: this.changeMetricGraphMode,
bestTrialEntries,
changeEntries: this.changeEntries,
updateOverviewPage: this.updateOverviewPage
}}
>
<TrialConfigButton />
</AppContext.Provider>
{/* if api has error field, show error message */} {/* if api has error field, show error message */}
{errorList.map( {errorList.map(
(item, key) => (item, key) =>
...@@ -160,6 +188,8 @@ class App extends React.Component<{}, AppState> { ...@@ -160,6 +188,8 @@ class App extends React.Component<{}, AppState> {
experimentUpdateBroadcast, experimentUpdateBroadcast,
trialsUpdateBroadcast, trialsUpdateBroadcast,
metricGraphMode, metricGraphMode,
maxDurationUnit,
changeMaxDurationUnit: this.changeMaxDurationUnit,
changeMetricGraphMode: this.changeMetricGraphMode, changeMetricGraphMode: this.changeMetricGraphMode,
bestTrialEntries, bestTrialEntries,
changeEntries: this.changeEntries, changeEntries: this.changeEntries,
......
...@@ -10,7 +10,8 @@ import { ReBasicInfo } from './overview/experiment/BasicInfo'; ...@@ -10,7 +10,8 @@ import { ReBasicInfo } from './overview/experiment/BasicInfo';
import { ExpDuration } from './overview/count/ExpDuration'; import { ExpDuration } from './overview/count/ExpDuration';
import { ExpDurationContext } from './overview/count/ExpDurationContext'; import { ExpDurationContext } from './overview/count/ExpDurationContext';
import { TrialCount } from './overview/count/TrialCount'; import { TrialCount } from './overview/count/TrialCount';
import { Command } from './overview/experiment/Command'; import { Command1 } from './overview/command/Command1';
import { Command2 } from './overview/command/Command2';
import { TitleContext } from './overview/TitleContext'; import { TitleContext } from './overview/TitleContext';
import { itemStyle1, itemStyleSucceed, itemStyle2, entriesOption } from './overview/overviewConst'; import { itemStyle1, itemStyleSucceed, itemStyle2, entriesOption } from './overview/overviewConst';
import '../static/style/overview/overview.scss'; import '../static/style/overview/overview.scss';
...@@ -66,7 +67,13 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -66,7 +67,13 @@ class Overview extends React.Component<{}, OverviewState> {
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
{(value): React.ReactNode => { {(value): React.ReactNode => {
const { metricGraphMode, bestTrialEntries, updateOverviewPage } = value; const {
metricGraphMode,
bestTrialEntries,
maxDurationUnit,
updateOverviewPage,
changeMaxDurationUnit
} = value;
const maxActive = metricGraphMode === 'max' ? 'active' : ''; const maxActive = metricGraphMode === 'max' ? 'active' : '';
const minActive = metricGraphMode === 'min' ? 'active' : ''; const minActive = metricGraphMode === 'min' ? 'active' : '';
return ( return (
...@@ -88,18 +95,29 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -88,18 +95,29 @@ class Overview extends React.Component<{}, OverviewState> {
<Title /> <Title />
</TitleContext.Provider> </TitleContext.Provider>
<ExpDurationContext.Provider <ExpDurationContext.Provider
value={{ maxExecDuration, execDuration, updateOverviewPage }} value={{
maxExecDuration,
execDuration,
updateOverviewPage,
maxDurationUnit,
changeMaxDurationUnit
}}
> >
<ExpDuration /> <ExpDuration />
</ExpDurationContext.Provider> </ExpDurationContext.Provider>
</div> </div>
<div className='empty' />
<div className='trialCount'> <div className='trialCount'>
<TitleContext.Provider value={{ text: 'Trial numbers', icon: 'NumberSymbol' }}> <TitleContext.Provider value={{ text: 'Trial numbers', icon: 'NumberSymbol' }}>
<Title /> <Title />
</TitleContext.Provider> </TitleContext.Provider>
<ExpDurationContext.Provider <ExpDurationContext.Provider
value={{ maxExecDuration, execDuration, updateOverviewPage }} value={{
maxExecDuration,
execDuration,
updateOverviewPage,
maxDurationUnit,
changeMaxDurationUnit
}}
> >
<TrialCount /> <TrialCount />
</ExpDurationContext.Provider> </ExpDurationContext.Provider>
...@@ -114,7 +132,6 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -114,7 +132,6 @@ class Overview extends React.Component<{}, OverviewState> {
</TitleContext.Provider> </TitleContext.Provider>
</div> </div>
<div className='topTrialTitle'> <div className='topTrialTitle'>
{/* <Stack horizontal horizontalAlign='space-between'> */}
<Stack horizontal horizontalAlign='end'> <Stack horizontal horizontalAlign='end'>
<DefaultButton <DefaultButton
onClick={this.clickMaxTop} onClick={this.clickMaxTop}
...@@ -152,8 +169,11 @@ class Overview extends React.Component<{}, OverviewState> { ...@@ -152,8 +169,11 @@ class Overview extends React.Component<{}, OverviewState> {
</Stack> </Stack>
<SuccessTable trialIds={bestTrials.map(trial => trial.info.id)} /> <SuccessTable trialIds={bestTrials.map(trial => trial.info.id)} />
</div> </div>
<div className='overviewCommand'> <div className='overviewCommand1'>
<Command /> <Command1 />
</div>
<div className='overviewCommand2'>
<Command2 />
</div> </div>
<div className='overviewChart'> <div className='overviewChart'>
<Stack horizontal> <Stack horizontal>
......
...@@ -82,7 +82,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> { ...@@ -82,7 +82,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
</Pivot> </Pivot>
</div> </div>
{/* trial table list */} {/* trial table list */}
<div style={{ backgroundColor: '#fff' }}> <div style={{ backgroundColor: '#fff', marginTop: 10 }}>
<TableList <TableList
tableSource={source} tableSource={source}
trialsUpdateBroadcast={this.context.trialsUpdateBroadcast} trialsUpdateBroadcast={this.context.trialsUpdateBroadcast}
......
...@@ -112,11 +112,15 @@ class KillJob extends React.Component<KillJobProps, KillJobState> { ...@@ -112,11 +112,15 @@ class KillJob extends React.Component<KillJobProps, KillJobState> {
setInitialFocus={true} setInitialFocus={true}
> >
<div className={styles.header}> <div className={styles.header}>
<p className={styles.title}>Kill trial</p> <p className={styles.title} style={{ color: '#333' }}>
Kill trial
</p>
</div> </div>
<div className={styles.inner}> <div className={styles.inner}>
<div> <div>
<p className={styles.subtext}>{prompString}</p> <p className={styles.subtext} style={{ color: '#333' }}>
{prompString}
</p>
</div> </div>
</div> </div>
<FocusZone> <FocusZone>
......
import React from 'react'; import React from 'react';
import { TooltipHost, Stack } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel'; import { EXPERIMENT } from '../../../static/datamodel';
import '../../../static/style/overview/command.scss'; import '../../../static/style/overview/command.scss';
export const Command = (): any => { export const Command1 = (): any => {
const clusterMetaData = EXPERIMENT.profile.params.clusterMetaData;
const tuner = EXPERIMENT.profile.params.tuner; const tuner = EXPERIMENT.profile.params.tuner;
const advisor = EXPERIMENT.profile.params.advisor; const advisor = EXPERIMENT.profile.params.advisor;
const assessor = EXPERIMENT.profile.params.assessor; const assessor = EXPERIMENT.profile.params.assessor;
let title = ''; let title = '';
let builtinName = ''; let builtinName = '';
let trialCommand = 'unknown';
if (tuner !== undefined) { if (tuner !== undefined) {
title = title.concat('Tuner'); title = title.concat('Tuner');
if (tuner.builtinTunerName !== undefined) { if (tuner.builtinTunerName !== undefined) {
...@@ -29,35 +26,15 @@ export const Command = (): any => { ...@@ -29,35 +26,15 @@ export const Command = (): any => {
builtinName = builtinName.concat(assessor.builtinAssessorName); builtinName = builtinName.concat(assessor.builtinAssessorName);
} }
} }
if (clusterMetaData !== undefined) {
for (const item of clusterMetaData) {
if (item.key === 'command') {
trialCommand = item.value;
}
}
}
return ( return (
<div className='command basic'> <div className='command basic'>
<div className='command1'> <div>
<p>Training platform</p> <p>Training platform</p>
<div className='nowrap'>{EXPERIMENT.profile.params.trainingServicePlatform}</div> <div className='nowrap'>{EXPERIMENT.profile.params.trainingServicePlatform}</div>
<p className='lineMargin'>{title}</p> <p className='lineMargin'>{title}</p>
<div className='nowrap'>{builtinName}</div> <div className='nowrap'>{builtinName}</div>
</div> </div>
<Stack className='command2'>
<p>Log directory</p>
<div className='nowrap'>
<TooltipHost content={EXPERIMENT.profile.logDir || 'unknown'} className='nowrap'>
{EXPERIMENT.profile.logDir || 'unknown'}
</TooltipHost>
</div>
<p className='lineMargin'>Trial command</p>
<div className='nowrap'>
<TooltipHost content={trialCommand || 'unknown'} className='nowrap'>
{trialCommand || 'unknown'}
</TooltipHost>
</div>
</Stack>
</div> </div>
); );
}; };
import React from 'react';
import { TooltipHost, DirectionalHint } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel';
import { TOOLTIP_BACKGROUND_COLOR } from '../../../static/const';
import '../../../static/style/overview/command.scss';
export const Command2 = (): any => {
const clusterMetaData = EXPERIMENT.profile.params.clusterMetaData;
let trialCommand = 'unknown';
if (clusterMetaData !== undefined) {
for (const item of clusterMetaData) {
if (item.key === 'command') {
trialCommand = item.value;
}
}
}
return (
<div className='command basic'>
<p>Log directory</p>
<div className='nowrap'>
<TooltipHost
content={EXPERIMENT.profile.logDir || 'unknown'}
className='nowrap'
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
styles: {
beak: { background: TOOLTIP_BACKGROUND_COLOR },
beakCurtain: { background: TOOLTIP_BACKGROUND_COLOR },
calloutMain: { background: TOOLTIP_BACKGROUND_COLOR }
}
}
}}
>
{EXPERIMENT.profile.logDir || 'unknown'}
</TooltipHost>
</div>
<p className='lineMargin'>Trial command</p>
<div className='nowrap'>
<TooltipHost
content={trialCommand || 'unknown'}
className='nowrap'
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
styles: {
beak: { background: TOOLTIP_BACKGROUND_COLOR },
beakCurtain: { background: TOOLTIP_BACKGROUND_COLOR },
calloutMain: { background: TOOLTIP_BACKGROUND_COLOR }
}
}
}}
>
{trialCommand || 'unknown'}
</TooltipHost>
</div>
</div>
);
};
import React, { useState, useCallback, useContext } from 'react'; import React, { useState, useCallback, useContext } from 'react';
import axios from 'axios'; import axios from 'axios';
import { Dropdown } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel'; import { EXPERIMENT } from '../../../static/datamodel';
import { AppContext } from '../../../App';
import { EditExpeParamContext } from './context'; import { EditExpeParamContext } from './context';
import { MANAGER_IP } from '../../../static/const'; import { durationUnit } from '../overviewConst';
import { convertTimeToSecond } from '../../../static/function'; import { MANAGER_IP, MAX_TRIAL_NUMBERS } from '../../../static/const';
import { Edit, CheckMark, Cancel } from '../../buttons/Icon'; import { Edit, CheckMark, Cancel } from '../../buttons/Icon';
import MessageInfo from '../../modals/MessageInfo'; import MessageInfo from '../../modals/MessageInfo';
import '../../../static/style/overview/count.scss'; import '../../../static/style/overview/count.scss';
...@@ -28,12 +30,14 @@ export const EditExperimentParam = (): any => { ...@@ -28,12 +30,14 @@ export const EditExperimentParam = (): any => {
const { title, field, editType, maxExecDuration, maxTrialNum, trialConcurrency, updateOverviewPage } = useContext( const { title, field, editType, maxExecDuration, maxTrialNum, trialConcurrency, updateOverviewPage } = useContext(
EditExpeParamContext EditExpeParamContext
); );
const { maxDurationUnit, changeMaxDurationUnit } = useContext(AppContext);
const [unit, setUnit] = useState(maxDurationUnit);
let defaultVal = ''; let defaultVal = '';
let editVal = ''; let editVal = '';
if (title === 'Max duration') { if (title === 'Max duration') {
defaultVal = maxExecDuration; defaultVal = maxExecDuration;
editVal = maxExecDuration; editVal = maxExecDuration;
} else if (title === 'Max trial numbers') { } else if (title === MAX_TRIAL_NUMBERS) {
defaultVal = maxTrialNum.toString(); defaultVal = maxTrialNum.toString();
editVal = maxTrialNum.toString(); editVal = maxTrialNum.toString();
} else { } else {
...@@ -46,32 +50,64 @@ export const EditExperimentParam = (): any => { ...@@ -46,32 +50,64 @@ export const EditExperimentParam = (): any => {
setEditValInput(event.target.value); setEditValInput(event.target.value);
} }
function cancelEdit(): void { function showMessageInfo(info: string, typeInfo: string): any {
setEditValInput(defaultVal); setInfo(info);
showPencil(); setTypeInfo(typeInfo);
showSucceedInfo();
setTimeout(hideSucceedInfo, 2000);
}
function updateUnit(event: React.FormEvent<HTMLDivElement>, item: any): void {
if (item !== undefined) {
setUnit(item.key);
}
} }
async function confirmEdit(): Promise<void> { async function confirmEdit(): Promise<void> {
const isMaxDuration = title === 'Max duration'; const isMaxDuration = title === 'Max duration';
const newProfile = Object.assign({}, EXPERIMENT.profile); const newProfile = Object.assign({}, EXPERIMENT.profile);
let beforeParam = ''; let beforeParam = '';
if (!isMaxDuration && !editInputVal.match(/^[1-9]\d*$/)) { if (isMaxDuration) {
showMessageInfo('Please enter a positive integer!', 'error'); if (!editInputVal.match(/^\d+(?=\.{0,1}\d+$|$)/)) {
return; showMessageInfo('Please enter a number!', 'error');
setEditValInput(defaultVal);
return;
}
} else {
if (!editInputVal.match(/^[1-9]\d*$/)) {
showMessageInfo('Please enter a positive integer!', 'error');
setEditValInput(defaultVal);
return;
}
} }
if (isMaxDuration) { if (isMaxDuration) {
beforeParam = maxExecDuration; beforeParam = maxExecDuration;
} else if (title === 'Max trial numbers') { } else if (title === MAX_TRIAL_NUMBERS) {
beforeParam = maxTrialNum.toString(); beforeParam = maxTrialNum.toString();
} else { } else {
beforeParam = trialConcurrency.toString(); beforeParam = trialConcurrency.toString();
} }
if (editInputVal === beforeParam) { if (editInputVal === beforeParam) {
showMessageInfo(`Trial ${field} has not changed`, 'error'); if (isMaxDuration) {
return; if (maxDurationUnit === unit) {
showMessageInfo(`Trial ${field} has not changed`, 'error');
return;
}
} else {
showMessageInfo(`Trial ${field} has not changed`, 'error');
return;
}
} }
if (isMaxDuration) { if (isMaxDuration) {
newProfile.params[field] = convertTimeToSecond(editInputVal); const maxDura = JSON.parse(editInputVal);
if (unit === 'm') {
newProfile.params[field] = maxDura * 60;
} else if (unit === 'h') {
newProfile.params[field] = maxDura * 3600;
} else {
newProfile.params[field] = maxDura * 24 * 60 * 60;
}
} else { } else {
newProfile.params[field] = parseInt(editInputVal, 10); newProfile.params[field] = parseInt(editInputVal, 10);
} }
...@@ -82,7 +118,8 @@ export const EditExperimentParam = (): any => { ...@@ -82,7 +118,8 @@ export const EditExperimentParam = (): any => {
params: { update_type: editType } params: { update_type: editType }
}); });
if (res.status === 200) { if (res.status === 200) {
showMessageInfo(`Successfully updated ${field}`, 'success'); showMessageInfo(`Successfully updated experiment's ${field}`, 'success');
changeMaxDurationUnit(unit);
} }
} catch (error) { } catch (error) {
if (error.response && error.response.data.error) { if (error.response && error.response.data.error) {
...@@ -94,54 +131,75 @@ export const EditExperimentParam = (): any => { ...@@ -94,54 +131,75 @@ export const EditExperimentParam = (): any => {
} else { } else {
showMessageInfo(`Failed to update trial ${field}\nUnknown error`, 'error'); showMessageInfo(`Failed to update trial ${field}\nUnknown error`, 'error');
} }
setEditValInput(defaultVal);
} }
showPencil(); showPencil();
updateOverviewPage(); updateOverviewPage();
} }
function showMessageInfo(info: string, typeInfo: string): any { function cancelEdit(): void {
setInfo(info); setEditValInput(defaultVal);
setTypeInfo(typeInfo); showPencil();
showSucceedInfo(); setUnit(maxDurationUnit);
setTimeout(hideSucceedInfo, 2000);
} }
return ( return (
<EditExpeParamContext.Consumer> <AppContext.Consumer>
{(value): React.ReactNode => { {(values): React.ReactNode => {
return ( return (
<React.Fragment> <EditExpeParamContext.Consumer>
<p>{value.title}</p> {(value): React.ReactNode => {
<div> let editClassName = '';
<input if (value.field === 'maxExecDuration') {
className={`${value.field} durationInput`} editClassName = isShowPencil ? 'noEditDuration' : 'editDuration';
ref={DurationInputRef} }
disabled={isShowPencil ? true : false} return (
value={editInputVal} <React.Fragment>
onChange={setInputVal} <div className={`${editClassName} editparam`}>
/> <span>{value.title}</span>
{isShowPencil && ( <input
<span className='edit' onClick={hidePencil}> className={`${value.field} editparam-Input`}
{Edit} ref={DurationInputRef}
</span> disabled={isShowPencil ? true : false}
)} value={editInputVal}
onChange={setInputVal}
{!isShowPencil && ( />
<span className='series'> {isShowPencil && title === 'Max duration' && (
<span className='confirm' onClick={confirmEdit}> <span>{values.maxDurationUnit}</span>
{CheckMark} )}
</span> {!isShowPencil && title === 'Max duration' && (
<span className='cancel' onClick={cancelEdit}> <Dropdown
{Cancel} selectedKey={unit}
</span> options={durationUnit}
</span> className='editparam-dropdown'
)} onChange={updateUnit}
/>
)}
{isShowPencil && (
<span className='edit' onClick={hidePencil}>
{Edit}
</span>
)}
{!isShowPencil && (
<span className='series'>
<span className='confirm' onClick={confirmEdit}>
{CheckMark}
</span>
<span className='cancel' onClick={cancelEdit}>
{Cancel}
</span>
</span>
)}
{isShowSucceedInfo && <MessageInfo className='info' typeInfo={typeInfo} info={info} />} {isShowSucceedInfo && (
</div> <MessageInfo className='info' typeInfo={typeInfo} info={info} />
</React.Fragment> )}
</div>
</React.Fragment>
);
}}
</EditExpeParamContext.Consumer>
); );
}} }}
</EditExpeParamContext.Consumer> </AppContext.Consumer>
); );
}; };
import React from 'react'; import React from 'react';
import { Stack, TooltipHost, ProgressIndicator } from '@fluentui/react'; import { Stack, ProgressIndicator, TooltipHost, DirectionalHint } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel'; import { EXPERIMENT } from '../../../static/datamodel';
import { CONTROLTYPE } from '../../../static/const'; import { CONTROLTYPE, TOOLTIP_BACKGROUND_COLOR } from '../../../static/const';
import { convertDuration } from '../../../static/function'; import { convertDuration, convertTimeAsUnit } from '../../../static/function';
import { EditExperimentParam } from './EditExperimentParam'; import { EditExperimentParam } from './EditExperimentParam';
import { ExpDurationContext } from './ExpDurationContext'; import { ExpDurationContext } from './ExpDurationContext';
import { EditExpeParamContext } from './context'; import { EditExpeParamContext } from './context';
import { durationItem1, durationItem2 } from './commonStyle';
import '../../../static/style/overview/count.scss'; import '../../../static/style/overview/count.scss';
const itemStyle1: React.CSSProperties = {
width: '62%',
height: 80
};
const itemStyle2: React.CSSProperties = {
width: '63%',
height: 80,
textAlign: 'right'
};
export const ExpDuration = (): any => ( export const ExpDuration = (): any => (
<ExpDurationContext.Consumer> <ExpDurationContext.Consumer>
{(value): React.ReactNode => { {(value): React.ReactNode => {
const { maxExecDuration, execDuration, updateOverviewPage } = value; const { maxExecDuration, execDuration, maxDurationUnit, updateOverviewPage } = value;
const tooltip = maxExecDuration - execDuration; const tooltip = maxExecDuration - execDuration;
const maxExecDurationStr = convertDuration(maxExecDuration);
const percent = execDuration / maxExecDuration; const percent = execDuration / maxExecDuration;
const execDurationStr = convertDuration(execDuration);
const maxExecDurationStr = convertTimeAsUnit(maxDurationUnit, maxExecDuration).toString();
return ( return (
<Stack horizontal className='ExpDuration'> <Stack horizontal className='ExpDuration'>
<div style={itemStyle1}> <div style={durationItem1}>
<TooltipHost content={`${convertDuration(tooltip)} remaining`}> <TooltipHost
<ProgressIndicator percentComplete={percent} barHeight={15} /> content={`${convertDuration(tooltip)} remaining`}
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
styles: {
beak: { background: TOOLTIP_BACKGROUND_COLOR },
beakCurtain: { background: TOOLTIP_BACKGROUND_COLOR },
calloutMain: { background: TOOLTIP_BACKGROUND_COLOR }
}
}
}}
>
<ProgressIndicator className={EXPERIMENT.status} percentComplete={percent} barHeight={15} />
</TooltipHost> </TooltipHost>
{/* execDuration / maxDuration: 20min / 1h */}
<div className='exp-progress'>
<span className={`${EXPERIMENT.status} bold`}>{execDurationStr}</span>
<span className='joiner'>/</span>
<span>{`${maxExecDurationStr} ${maxDurationUnit}`}</span>
</div>
</div> </div>
<div style={itemStyle2}> <div style={durationItem2}>
<Stack horizontal></Stack>
<EditExpeParamContext.Provider <EditExpeParamContext.Provider
value={{ value={{
editType: CONTROLTYPE[0], editType: CONTROLTYPE[0],
......
...@@ -3,5 +3,8 @@ export const ExpDurationContext = React.createContext({ ...@@ -3,5 +3,8 @@ export const ExpDurationContext = React.createContext({
maxExecDuration: 0, maxExecDuration: 0,
execDuration: 0, execDuration: 0,
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
updateOverviewPage: (): void => {} updateOverviewPage: (): void => {},
maxDurationUnit: 'm',
// eslint-disable-next-line @typescript-eslint/no-empty-function
changeMaxDurationUnit: (_val: 'd' | 'h' | 'm'): void => {}
}); });
import * as React from 'react'; import * as React from 'react';
import { Stack, TooltipHost, ProgressIndicator } from '@fluentui/react'; import { Stack, TooltipHost, ProgressIndicator, DirectionalHint } from '@fluentui/react';
import { EXPERIMENT, TRIALS } from '../../../static/datamodel'; import { EXPERIMENT, TRIALS } from '../../../static/datamodel';
import { CONTROLTYPE } from '../../../static/const'; import { CONTROLTYPE, TOOLTIP_BACKGROUND_COLOR, MAX_TRIAL_NUMBERS } from '../../../static/const';
import { EditExperimentParam } from './EditExperimentParam'; import { EditExperimentParam } from './EditExperimentParam';
import { EditExpeParamContext } from './context'; import { EditExpeParamContext } from './context';
import { ExpDurationContext } from './ExpDurationContext'; import { ExpDurationContext } from './ExpDurationContext';
import { trialCountItem1, trialCountItem2 } from './commonStyle';
const itemStyles: React.CSSProperties = {
width: '62%'
};
const itemStyle2: React.CSSProperties = {
width: '63%',
textAlign: 'right'
};
const itemStyle1: React.CSSProperties = {
width: '30%',
height: 50
};
const itemRunning: React.CSSProperties = {
width: '42%',
height: 56
};
export const TrialCount = (): any => { export const TrialCount = (): any => {
const count = TRIALS.countStatus(); const count = TRIALS.countStatus();
...@@ -30,8 +13,9 @@ export const TrialCount = (): any => { ...@@ -30,8 +13,9 @@ export const TrialCount = (): any => {
const stoppedCount = count.get('USER_CANCELED')! + count.get('SYS_CANCELED')! + count.get('EARLY_STOPPED')!; const stoppedCount = count.get('USER_CANCELED')! + count.get('SYS_CANCELED')! + count.get('EARLY_STOPPED')!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const bar2 = count.get('RUNNING')! + count.get('SUCCEEDED')! + count.get('FAILED')! + stoppedCount; const bar2 = count.get('RUNNING')! + count.get('SUCCEEDED')! + count.get('FAILED')! + stoppedCount;
const maxTrialNum = EXPERIMENT.profile.params.maxTrialNum;
// support type [0, 1], not 98% // support type [0, 1], not 98%
const bar2Percent = bar2 / EXPERIMENT.profile.params.maxTrialNum; const bar2Percent = bar2 / maxTrialNum;
return ( return (
<ExpDurationContext.Consumer> <ExpDurationContext.Consumer>
{(value): React.ReactNode => { {(value): React.ReactNode => {
...@@ -39,40 +23,36 @@ export const TrialCount = (): any => { ...@@ -39,40 +23,36 @@ export const TrialCount = (): any => {
return ( return (
<React.Fragment> <React.Fragment>
<Stack horizontal horizontalAlign='space-between' className='ExpDuration'> <Stack horizontal horizontalAlign='space-between' className='ExpDuration'>
<div style={itemStyles}> <div style={trialCountItem1}>
<TooltipHost content={bar2.toString()}> <TooltipHost
<ProgressIndicator percentComplete={bar2Percent} barHeight={15} /> content={bar2.toString()}
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
styles: {
beak: { background: TOOLTIP_BACKGROUND_COLOR },
beakCurtain: { background: TOOLTIP_BACKGROUND_COLOR },
calloutMain: { background: TOOLTIP_BACKGROUND_COLOR }
}
}
}}
>
<ProgressIndicator
className={EXPERIMENT.status}
percentComplete={bar2Percent}
barHeight={15}
/>
</TooltipHost> </TooltipHost>
<Stack horizontal className='mess'> <div className='exp-progress'>
<div style={itemRunning} className='basic'> <span className={`${EXPERIMENT.status} bold`}>{bar2}</span>
<p>Running</p> <span className='joiner'>/</span>
<div>{count.get('RUNNING')}</div> <span>{maxTrialNum}</span>
</div> </div>
<div style={itemStyle1} className='basic'>
<p>Failed</p>
<div>{count.get('FAILED')}</div>
</div>
<div style={itemStyle1} className='basic'>
<p>Stopped</p>
<div>{stoppedCount}</div>
</div>
</Stack>
<Stack horizontal horizontalAlign='space-between' className='mess'>
<div style={itemStyle1} className='basic'>
<p>Succeeded</p>
<div>{count.get('SUCCEEDED')}</div>
</div>
<div style={itemStyle1} className='basic'>
<p>Waiting</p>
<div>{count.get('WAITING')}</div>
</div>
</Stack>
</div> </div>
<div style={itemStyle2}> <div style={trialCountItem2}>
<EditExpeParamContext.Provider <EditExpeParamContext.Provider
value={{ value={{
title: 'Max trial numbers', title: MAX_TRIAL_NUMBERS,
field: 'maxTrialNum', field: 'maxTrialNum',
editType: CONTROLTYPE[1], editType: CONTROLTYPE[1],
maxExecDuration: '', maxExecDuration: '',
...@@ -81,7 +61,9 @@ export const TrialCount = (): any => { ...@@ -81,7 +61,9 @@ export const TrialCount = (): any => {
updateOverviewPage updateOverviewPage
}} }}
> >
<EditExperimentParam /> <div className='maxTrialNum'>
<EditExperimentParam />
</div>
</EditExpeParamContext.Provider> </EditExpeParamContext.Provider>
<EditExpeParamContext.Provider <EditExpeParamContext.Provider
value={{ value={{
...@@ -99,6 +81,28 @@ export const TrialCount = (): any => { ...@@ -99,6 +81,28 @@ export const TrialCount = (): any => {
</EditExpeParamContext.Provider> </EditExpeParamContext.Provider>
</div> </div>
</Stack> </Stack>
<Stack horizontal horizontalAlign='space-between' className='mess'>
<div className='basic'>
<p>Running</p>
<div>{count.get('RUNNING')}</div>
</div>
<div className='basic'>
<p>Succeeded</p>
<div>{count.get('SUCCEEDED')}</div>
</div>
<div className='basic'>
<p>Stopped</p>
<div>{stoppedCount}</div>
</div>
<div className='basic'>
<p>Failed</p>
<div>{count.get('FAILED')}</div>
</div>
<div className='basic'>
<p>Waiting</p>
<div>{count.get('WAITING')}</div>
</div>
</Stack>
</React.Fragment> </React.Fragment>
); );
}} }}
......
const durationItem1: React.CSSProperties = {
width: '33%'
};
const durationItem2: React.CSSProperties = {
width: '52%',
paddingLeft: '15%'
};
const trialCountItem1: React.CSSProperties = {
width: '33%'
};
const trialCountItem2: React.CSSProperties = {
width: '52%'
};
export { durationItem1, durationItem2, trialCountItem1, trialCountItem2 };
...@@ -24,61 +24,63 @@ export const ReBasicInfo = (): any => { ...@@ -24,61 +24,63 @@ export const ReBasicInfo = (): any => {
return ( return (
<div> <div>
<div className='basic'> <div className='basic'>
<p>ID: {EXPERIMENT.profile.id}</p> <p>
ID: <span>{EXPERIMENT.profile.id}</span>
</p>
<div>{EXPERIMENT.profile.params.experimentName}</div> <div>{EXPERIMENT.profile.params.experimentName}</div>
</div> </div>
<div className='basic'> <div className='basic'>
<Stack className='basic'> <p>Status</p>
<p>Status</p> <Stack horizontal className='status'>
<Stack horizontal className='status'> <span className={`${EXPERIMENT.status} status-text`}>{EXPERIMENT.status}</span>
<span className={`${EXPERIMENT.status} status-text`}>{EXPERIMENT.status}</span> {EXPERIMENT.status === 'ERROR' ? (
{EXPERIMENT.status === 'ERROR' ? ( <div>
<div> <div className={styles.buttonArea} ref={ref}>
<div className={styles.buttonArea} ref={ref}> <IconButton
<IconButton iconProps={{ iconName: 'info' }}
iconProps={{ iconName: 'info' }} onClick={isCalloutVisible ? onDismiss : showCallout}
onClick={isCalloutVisible ? onDismiss : showCallout} />
/>
</div>
{isCalloutVisible && (
<Callout
className={styles.callout}
ariaLabelledBy={labelId}
ariaDescribedBy={descriptionId}
role='alertdialog'
gapSpace={0}
target={ref}
onDismiss={onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title} id={labelId}>
Error
</p>
</div>
<div className={styles.inner}>
<p className={styles.subtext} id={descriptionId}>
{EXPERIMENT.error}
</p>
<div className={styles.actions}>
<Link className={styles.link} onClick={ShowLogDrawer}>
Learn about
</Link>
</div>
</div>
</Callout>
)}
</div> </div>
) : null} {isCalloutVisible && (
</Stack> <Callout
className={styles.callout}
ariaLabelledBy={labelId}
ariaDescribedBy={descriptionId}
role='alertdialog'
gapSpace={0}
target={ref}
onDismiss={onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title} id={labelId} style={{ color: '#333' }}>
Error
</p>
</div>
<div className={styles.inner}>
<p className={styles.subtext} id={descriptionId} style={{ color: '#333' }}>
{EXPERIMENT.error}
</p>
<div className={styles.actions}>
<Link className={styles.link} onClick={ShowLogDrawer}>
Learn about
</Link>
</div>
</div>
</Callout>
)}
</div>
) : null}
</Stack> </Stack>
</div> </div>
<div className='basic'> <div className='basic'>
<BestMetricContext.Consumer> <BestMetricContext.Consumer>
{(value): React.ReactNode => ( {(value): React.ReactNode => (
<Stack> <Stack className='bestMetric'>
<p>Best metric</p> <p>Best metric</p>
<div>{isNaN(value.bestAccuracy) ? 'N/A' : value.bestAccuracy.toFixed(6)}</div> <div className={EXPERIMENT.status}>
{isNaN(value.bestAccuracy) ? 'N/A' : value.bestAccuracy.toFixed(6)}
</div>
</Stack> </Stack>
)} )}
</BestMetricContext.Consumer> </BestMetricContext.Consumer>
......
...@@ -18,4 +18,10 @@ const entriesOption = [ ...@@ -18,4 +18,10 @@ const entriesOption = [
{ key: '100', text: '100' } { key: '100', text: '100' }
]; ];
export { itemStyle1, itemStyleSucceed, itemStyle2, entriesOption }; const durationUnit = [
{ key: 'm', text: 'm' },
{ key: 'h', text: 'h' },
{ key: 'd', text: 'd' }
];
export { itemStyle1, itemStyleSucceed, itemStyle2, entriesOption, durationUnit };
...@@ -70,40 +70,41 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> ...@@ -70,40 +70,41 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
</div> </div>
</div> </div>
); );
columns = [ columns = [
{ {
name: 'Trial No.', name: 'Trial No.',
key: 'sequenceId', key: 'sequenceId',
fieldName: 'sequenceId', // required! fieldName: 'sequenceId', // required!
minWidth: (window.innerWidth * 0.333 - 150) / 5, minWidth: 50,
maxWidth: (window.innerWidth * 0.333 - 150) / 5, maxWidth: 87,
isResizable: true, isResizable: true,
data: 'number', data: 'number',
onColumnClick: this.onColumnClick onColumnClick: this.onColumnClick,
onRender: (item: any): React.ReactNode => <div className='succeed-padding'>{item.sequenceId}</div>
}, },
{ {
name: 'ID', name: 'ID',
key: 'id', key: 'id',
fieldName: 'id', fieldName: 'id',
minWidth: (window.innerWidth * 0.333 - 150) / 5, minWidth: 50,
maxWidth: (window.innerWidth * 0.333 - 150) / 5, maxWidth: 87,
isResizable: true, isResizable: true,
className: 'tableHead leftTitle', className: 'tableHead leftTitle',
data: 'string', data: 'string',
onColumnClick: this.onColumnClick onColumnClick: this.onColumnClick,
onRender: (item: any): React.ReactNode => <div className='succeed-padding'>{item.id}</div>
}, },
{ {
name: 'Duration', name: 'Duration',
key: 'duration', key: 'duration',
minWidth: (window.innerWidth * 0.333 - 150) / 5, minWidth: 65,
maxWidth: (window.innerWidth * 0.333 - 150) / 5, maxWidth: 150,
isResizable: true, isResizable: true,
fieldName: 'duration', fieldName: 'duration',
data: 'number', data: 'number',
onColumnClick: this.onColumnClick, onColumnClick: this.onColumnClick,
onRender: (item: any): React.ReactNode => ( onRender: (item: any): React.ReactNode => (
<div className='durationsty'> <div className='durationsty succeed-padding'>
<div>{convertDuration(item.duration)}</div> <div>{convertDuration(item.duration)}</div>
</div> </div>
) )
...@@ -111,26 +112,24 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> ...@@ -111,26 +112,24 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{ {
name: 'Status', name: 'Status',
key: 'status', key: 'status',
minWidth: (window.innerWidth * 0.333 - 150) / 5, minWidth: 80,
maxWidth: (window.innerWidth * 0.333 - 150) / 5, maxWidth: 150,
isResizable: true, isResizable: true,
fieldName: 'status', fieldName: 'status',
onRender: (item: any): React.ReactNode => { onRender: (item: any): React.ReactNode => (
return <div className={`${item.status} commonStyle`}>{item.status}</div>; <div className={`${item.status} commonStyle succeed-padding`}>{item.status}</div>
} )
}, },
{ {
name: 'Default metric', name: 'Default metric',
key: 'accuracy', key: 'accuracy',
fieldName: 'accuracy', fieldName: 'accuracy',
minWidth: (window.innerWidth * 0.333 - 200) / 5, minWidth: 100,
// maxWidth: (window.innerWidth * 0.333 - 150) / 5, maxWidth: 160,
isResizable: true, isResizable: true,
data: 'number', data: 'number',
onColumnClick: this.onColumnClick, onColumnClick: this.onColumnClick,
onRender: (item: any): React.ReactNode => { onRender: (item: any): React.ReactNode => <DefaultMetric trialId={item.id} />
return <DefaultMetric trialId={item.id} />;
}
} }
]; ];
......
...@@ -13,7 +13,7 @@ class DefaultMetric extends React.Component<DefaultMetricProps, {}> { ...@@ -13,7 +13,7 @@ class DefaultMetric extends React.Component<DefaultMetricProps, {}> {
render(): React.ReactNode { render(): React.ReactNode {
const accuracy = TRIALS.getTrial(this.props.trialId).accuracy; const accuracy = TRIALS.getTrial(this.props.trialId).accuracy;
return <div>{accuracy !== undefined ? formatAccuracy(accuracy) : '--'}</div>; return <div className='succeed-padding'>{accuracy !== undefined ? formatAccuracy(accuracy) : '--'}</div>;
} }
} }
......
...@@ -3,7 +3,8 @@ import { Stack, Panel, Pivot, PivotItem, PrimaryButton } from '@fluentui/react'; ...@@ -3,7 +3,8 @@ import { Stack, Panel, Pivot, PivotItem, PrimaryButton } from '@fluentui/react';
import { EXPERIMENT } from '../../../static/datamodel'; import { EXPERIMENT } from '../../../static/datamodel';
import MonacoEditor from 'react-monaco-editor'; import MonacoEditor from 'react-monaco-editor';
import { MONACO } from '../../../static/const'; import { MONACO } from '../../../static/const';
import { convertDuration } from '../../../static/function'; import { AppContext } from '../../../App';
import { convertDuration, convertTimeAsUnit } from '../../../static/function';
import { prettyStringify } from '../../../static/json_util'; import { prettyStringify } from '../../../static/json_util';
import lodash from 'lodash'; import lodash from 'lodash';
import '../../../static/style/logDrawer.scss'; import '../../../static/style/logDrawer.scss';
...@@ -15,6 +16,7 @@ interface LogDrawerProps { ...@@ -15,6 +16,7 @@ interface LogDrawerProps {
interface LogDrawerState { interface LogDrawerState {
panelInnerHeight: number; panelInnerHeight: number;
innerWidth: number;
} }
class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> { class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
...@@ -22,13 +24,15 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> { ...@@ -22,13 +24,15 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
super(props); super(props);
this.state = { this.state = {
panelInnerHeight: window.innerHeight panelInnerHeight: window.innerHeight,
innerWidth: window.innerWidth
}; };
} }
setLogDrawerHeight(): void { // use arrow function for change window size met error: this.setState is not a function
this.setState(() => ({ panelInnerHeight: window.innerHeight })); setLogDrawerHeight = (): void => {
} this.setState(() => ({ panelInnerHeight: window.innerHeight, innerWidth: window.innerWidth }));
};
async componentDidMount(): Promise<void> { async componentDidMount(): Promise<void> {
window.addEventListener('resize', this.setLogDrawerHeight); window.addEventListener('resize', this.setLogDrawerHeight);
...@@ -40,7 +44,7 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> { ...@@ -40,7 +44,7 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
render(): React.ReactNode { render(): React.ReactNode {
const { hideConfigPanel, activeTab } = this.props; const { hideConfigPanel, activeTab } = this.props;
const { panelInnerHeight } = this.state; const { panelInnerHeight, innerWidth } = this.state;
// [marginTop 16px] + [Search space 46px] + // [marginTop 16px] + [Search space 46px] +
// button[height: 32px, marginTop: 45px, marginBottom: 25px] + [padding-bottom: 20px] // button[height: 32px, marginTop: 45px, marginBottom: 25px] + [padding-bottom: 20px]
const monacoEditorHeight = panelInnerHeight - 184; const monacoEditorHeight = panelInnerHeight - 184;
...@@ -58,45 +62,61 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> { ...@@ -58,45 +62,61 @@ class TrialConfigPanel extends React.Component<LogDrawerProps, LogDrawerState> {
}; };
const profile = lodash.cloneDeep(EXPERIMENT.profile); const profile = lodash.cloneDeep(EXPERIMENT.profile);
profile.execDuration = convertDuration(profile.execDuration); profile.execDuration = convertDuration(profile.execDuration);
profile.params.maxExecDuration = convertDuration(profile.params.maxExecDuration);
const showProfile = JSON.stringify(profile, filter, 2); const prettyWidth = innerWidth > 1400 ? 100 : 60;
return ( return (
<Stack> <AppContext.Consumer>
<Panel {(value): React.ReactNode => {
isOpen={true} const unit = value.maxDurationUnit;
hasCloseButton={false} profile.params.maxExecDuration = `${convertTimeAsUnit(
isFooterAtBottom={true} unit,
isLightDismiss={true} profile.params.maxExecDuration
onLightDismissClick={hideConfigPanel} )}${unit}`;
> const showProfile = JSON.stringify(profile, filter, 2);
<div className='log-tab-body'> return (
<Pivot initialSelectedKey={activeTab} style={{ minHeight: 190, paddingTop: '16px' }}> <Stack>
<PivotItem headerText='Search space' itemKey='search space'> <Panel
<MonacoEditor isOpen={true}
height={monacoEditorHeight} hasCloseButton={false}
language='json' isFooterAtBottom={true}
theme='vs-light' isLightDismiss={true}
value={prettyStringify(EXPERIMENT.searchSpace, 300, 2)} onLightDismissClick={hideConfigPanel}
options={MONACO} >
/> <div className='log-tab-body'>
</PivotItem> <Pivot
<PivotItem headerText='Config' itemKey='config'> initialSelectedKey={activeTab}
<div className='profile'> style={{ minHeight: 190, paddingTop: '16px' }}
<MonacoEditor >
width='100%' <PivotItem headerText='Search space' itemKey='search space'>
height={monacoEditorHeight} <MonacoEditor
language='json' height={monacoEditorHeight}
theme='vs-light' language='json'
value={showProfile} theme='vs-light'
options={MONACO} value={prettyStringify(EXPERIMENT.searchSpace, prettyWidth, 2)}
/> options={MONACO}
/>
</PivotItem>
<PivotItem headerText='Config' itemKey='config'>
<div className='profile'>
<MonacoEditor
width='100%'
height={monacoEditorHeight}
language='json'
theme='vs-light'
value={showProfile}
options={MONACO}
/>
</div>
</PivotItem>
</Pivot>
</div> </div>
</PivotItem> <PrimaryButton text='Close' className='configClose' onClick={hideConfigPanel} />
</Pivot> </Panel>
</div> </Stack>
<PrimaryButton text='Close' className='configClose' onClick={hideConfigPanel} /> );
</Panel> }}
</Stack> </AppContext.Consumer>
); );
} }
} }
......
...@@ -9,10 +9,12 @@ import { ...@@ -9,10 +9,12 @@ import {
SelectionMode, SelectionMode,
Stack, Stack,
StackItem, StackItem,
TooltipHost TooltipHost,
DirectionalHint
} from '@fluentui/react'; } from '@fluentui/react';
import React from 'react'; import React from 'react';
import { EXPERIMENT, TRIALS } from '../../static/datamodel'; import { EXPERIMENT, TRIALS } from '../../static/datamodel';
import { TOOLTIP_BACKGROUND_COLOR } from '../../static/const';
import { convertDuration, formatTimestamp } from '../../static/function'; import { convertDuration, formatTimestamp } from '../../static/function';
import { TableObj } from '../../static/interface'; import { TableObj } from '../../static/interface';
import '../../static/style/search.scss'; import '../../static/style/search.scss';
...@@ -316,7 +318,19 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -316,7 +318,19 @@ class TableList extends React.Component<TableListProps, TableListState> {
...((k.startsWith('metric/') || k.startsWith('space/')) && { ...((k.startsWith('metric/') || k.startsWith('space/')) && {
// show tooltip // show tooltip
onRender: (record): React.ReactNode => ( onRender: (record): React.ReactNode => (
<TooltipHost content={record[k]}> <TooltipHost
content={record[k]}
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
styles: {
beak: { background: TOOLTIP_BACKGROUND_COLOR },
beakCurtain: { background: TOOLTIP_BACKGROUND_COLOR },
calloutMain: { background: TOOLTIP_BACKGROUND_COLOR }
}
}
}}
>
<div className='ellipsis'>{record[k]}</div> <div className='ellipsis'>{record[k]}</div>
</TooltipHost> </TooltipHost>
) )
...@@ -324,7 +338,19 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -324,7 +338,19 @@ class TableList extends React.Component<TableListProps, TableListState> {
...(k === 'latestAccuracy' && { ...(k === 'latestAccuracy' && {
// FIXME: this is ad-hoc // FIXME: this is ad-hoc
onRender: (record): React.ReactNode => ( onRender: (record): React.ReactNode => (
<TooltipHost content={record._formattedLatestAccuracy}> <TooltipHost
content={record._formattedLatestAccuracy}
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
styles: {
beak: { background: TOOLTIP_BACKGROUND_COLOR },
beakCurtain: { background: TOOLTIP_BACKGROUND_COLOR },
calloutMain: { background: TOOLTIP_BACKGROUND_COLOR }
}
}
}}
>
<div className='ellipsis'>{record._formattedLatestAccuracy}</div> <div className='ellipsis'>{record._formattedLatestAccuracy}</div>
</TooltipHost> </TooltipHost>
) )
......
import React, { lazy, Suspense } from 'react'; import React, { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import App from './App'; import App from './App';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Overview = lazy(() => import('./components/Overview')); const Overview = lazy(() => import('./components/Overview'));
const TrialsDetail = lazy(() => import('./components/TrialsDetail')); const TrialsDetail = lazy(() => import('./components/TrialsDetail'));
import './index.css'; import './index.css';
...@@ -19,9 +19,9 @@ ReactDOM.render( ...@@ -19,9 +19,9 @@ ReactDOM.render(
</div> </div>
} }
> >
<Route path='/' component={Overview} exact />
<Route path='/oview' component={Overview} /> <Route path='/oview' component={Overview} />
<Route path='/detail' component={TrialsDetail} /> <Route path='/detail' component={TrialsDetail} />
<Route path='/' render={(): React.ReactNode => <Redirect to={{ pathname: '/oview' }} />} />
</Suspense> </Suspense>
</Switch> </Switch>
</App> </App>
......
...@@ -57,6 +57,9 @@ const SUPPORTED_SEARCH_SPACE_TYPE = [ ...@@ -57,6 +57,9 @@ const SUPPORTED_SEARCH_SPACE_TYPE = [
'qlognormal' 'qlognormal'
]; ];
const TOOLTIP_BACKGROUND_COLOR = '#484848';
const MAX_TRIAL_NUMBERS = 'Max trial No.';
export { export {
MANAGER_IP, MANAGER_IP,
DOWNLOAD_IP, DOWNLOAD_IP,
...@@ -71,5 +74,7 @@ export { ...@@ -71,5 +74,7 @@ export {
METRIC_GROUP_UPDATE_THRESHOLD, METRIC_GROUP_UPDATE_THRESHOLD,
METRIC_GROUP_UPDATE_SIZE, METRIC_GROUP_UPDATE_SIZE,
CONCURRENCYTOOLTIP, CONCURRENCYTOOLTIP,
SUPPORTED_SEARCH_SPACE_TYPE SUPPORTED_SEARCH_SPACE_TYPE,
TOOLTIP_BACKGROUND_COLOR,
MAX_TRIAL_NUMBERS
}; };
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