Skip to content

Commit

Permalink
Merge pull request #329 from Kitware/placeholder-pages
Browse files Browse the repository at this point in the history
Placeholder pages
  • Loading branch information
TristanWright committed Apr 7, 2016
2 parents 0111893 + eaa74b7 commit bba871f
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 66 deletions.
38 changes: 26 additions & 12 deletions src/pages/Preferences/AWS/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import ActiveList from '../../../panels/ActiveList';
import ButtonBar from '../../../panels/ButtonBar';
import Toolbar from '../../../panels/Toolbar';
import React from 'react';
import EmptyPlaceholder from '../../../panels/EmptyPlaceholder';
import { breadcrumb } from '..';

import theme from 'HPCCloudStyle/Theme.mcss';
import style from 'HPCCloudStyle/PageWithMenu.mcss';

import get from 'mout/src/object/get';
Expand Down Expand Up @@ -103,6 +105,29 @@ const AWSPrefs = React.createClass({
const { active, list, error, buttonsDisabled } = this.props;
const activeData = active < list.length ? list[active] : null;

let content = null;
if (list.length) {
content = (<div className={ style.content }>
<AWSForm
data={activeData}
onChange={ this.changeItem }
/>
<ButtonBar
visible={!!activeData}
onAction={ this.formAction }
error={ this.state._error || error }
actions={getActions(buttonsDisabled)}
/>
</div>);
} else {
content = (<EmptyPlaceholder phrase={
<span>
There are no EC2 Profiles available <br />
You can create some with the <i className={theme.addIcon}></i> above
</span> }
/>);
}

return (
<div className={ style.rootContainer }>
<Toolbar
Expand All @@ -118,18 +143,7 @@ const AWSPrefs = React.createClass({
active={active}
list={list}
/>
<div className={ style.content }>
<AWSForm
data={activeData}
onChange={ this.changeItem }
/>
<ButtonBar
visible={!!activeData}
onAction={ this.formAction }
error={ this.state._error || error }
actions={getActions(buttonsDisabled)}
/>
</div>
{ content }
</div>
</div>);
},
Expand Down
51 changes: 33 additions & 18 deletions src/pages/Preferences/Cluster/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import ClusterForm from './ClusterForm';
import ActiveList from '../../../panels/ActiveList';
import Toolbar from '../../../panels/Toolbar';
import ButtonBar from '../../../panels/ButtonBar';
import EmptyPlaceholder from '../../../panels/EmptyPlaceholder';
import PresetSelector from '../PresetSelector';
import React from 'react';
import { breadcrumb } from '..';

import theme from 'HPCCloudStyle/Theme.mcss';
import style from 'HPCCloudStyle/PageWithMenu.mcss';

import get from 'mout/src/object/get';
Expand Down Expand Up @@ -43,6 +45,7 @@ const ClusterPrefs = React.createClass({
simulations: React.PropTypes.object,
taskflows: React.PropTypes.array,
buttonsDisabled: React.PropTypes.bool,

onUpdateItem: React.PropTypes.func,
onActiveChange: React.PropTypes.func,
onApplyPreset: React.PropTypes.func,
Expand Down Expand Up @@ -130,6 +133,35 @@ const ClusterPrefs = React.createClass({
const { active, list, error, buttonsDisabled, presetNames } = this.props;
const activeData = active < list.length ? list[active] : null;

let content = null;
if (list && list.length) {
content = (<div className={ style.content }>
<ClusterForm
data={activeData}
onChange={ this.changeItem }
/>
<ButtonBar
visible={!!activeData}
onAction={ this.formAction }
error={ error || this.state._error }
actions={getActions(buttonsDisabled, activeData && activeData.config.ssh.publicKey && activeData.status !== 'running')}
>
<PresetSelector
contents={presetNames}
onChange={this.presetChange}
value={''}
/>
</ButtonBar>
</div>);
} else {
content = (<EmptyPlaceholder phrase={
<span>
There are no Clusters available <br />
You can create some with the <i className={theme.addIcon}></i> above
</span> }
/>);
}

return (
<div className={ style.rootContainer }>
<Toolbar
Expand All @@ -145,24 +177,7 @@ const ClusterPrefs = React.createClass({
active={active}
list={list}
/>
<div className={ style.content }>
<ClusterForm
data={activeData}
onChange={ this.changeItem }
/>
<ButtonBar
visible={!!activeData}
onAction={ this.formAction }
error={ error || this.state._error }
actions={getActions(buttonsDisabled, activeData && activeData.config.ssh.publicKey && activeData.status !== 'running')}
>
<PresetSelector
contents={presetNames}
onChange={this.presetChange}
value={''}
/>
</ButtonBar>
</div>
{ content }
</div>
</div>);
},
Expand Down
15 changes: 13 additions & 2 deletions src/pages/Project/All/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import merge from 'mout/src/object/merge';
import React from 'react';
import TableListing from '../../../panels/TableListing';
import EmptyPlaceholder from '../../../panels/EmptyPlaceholder';
import { ProjectHelper } from '../../../utils/AccessHelper';

import breadCrumbStyle from 'HPCCloudStyle/Theme.mcss';
import theme from 'HPCCloudStyle/Theme.mcss';

import { connect } from 'react-redux';
import { dispatch } from '../../../redux';
Expand Down Expand Up @@ -57,13 +58,23 @@ const ProjectAll = React.createClass({
breadcrumb={{
paths: ['/'],
icons: [
breadCrumbStyle.breadCrumbRootIcon,
theme.breadCrumbRootIcon,
] }}
location={ this.props.location }
accessHelper={ ProjectHelper }
items={ this.props.projects }
onAction={ this.onAction }
title="Projects"
placeholder={
<EmptyPlaceholder phrase={
<div>
<h3>Welcome to HPC Cloud</h3>
<span>You haven't created any projects yet<br />
Add one with the <i className={ theme.addIcon }></i> above.
</span>
</div>}
/>
}
/>);
},
});
Expand Down
14 changes: 11 additions & 3 deletions src/pages/Project/View/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { SimulationHelper } from '../../../utils/AccessHelper';
import TableListing from '../../../panels/TableListing';
import EmptyPlaceholder from '../../../panels/EmptyPlaceholder';

import breadCrumbStyle from 'HPCCloudStyle/Theme.mcss';
import theme from 'HPCCloudStyle/Theme.mcss';

import { connect } from 'react-redux';
import { dispatch } from '../../../redux';
Expand Down Expand Up @@ -59,14 +60,21 @@ const ProjectView = React.createClass({
breadcrumb={{
paths: ['/', `/View/Project/${this.props.params.id}`],
icons: [
breadCrumbStyle.breadCrumbRootIcon,
breadCrumbStyle.breadCrumbProjectIcon,
theme.breadCrumbRootIcon,
theme.breadCrumbProjectIcon,
] }}
location={ this.props.location }
accessHelper={ SimulationHelper }
items={ this.props.simulations }
onAction={ this.onAction }
title={ `${this.props.project.name} / Simulations` }
placeholder={
<EmptyPlaceholder phrase={
<span>There are no simulations for this project<br />
You can add some with the <i className={ theme.addIcon }></i> above.
</span>}
/>
}
/>);
},
});
Expand Down
24 changes: 24 additions & 0 deletions src/panels/EmptyPlaceholder/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import style from 'HPCCloudStyle/Layout.mcss';

// used for empty placeholders on some pages

const classes = [
style.verticalFlexContainer,
style.fullWidth,
style.halfHeight,
style.horizontalCenter,
style.verticalCenter,
style.textCenter,
].join(' ');

const placeholder = (props) => (
<div className={ classes }>
{ props.phrase }
</div>
);

placeholder.displayName = 'EmptyPlaceholder';
placeholder.propTypes = { phrase: React.PropTypes.string };

export default placeholder;
69 changes: 39 additions & 30 deletions src/panels/TableListing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default React.createClass({
location: React.PropTypes.object,
onAction: React.PropTypes.func,
title: React.PropTypes.string,
placeholder: React.PropTypes.object,
},

contextTypes: {
Expand Down Expand Up @@ -111,6 +112,43 @@ export default React.createClass({
updateQuery(this.props.location.query.filter);
const filteredList = this.props.items.filter(itemFilter);
const helper = this.props.accessHelper;
let content = null;

if (this.props.items.length) {
content = (
<table className={ style.table }>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Description</th>
<th>Created</th>
<th>Updated</th>
<th></th>
</tr>
</thead>
<tbody>
{ filteredList.map((item, index) =>
<tr key={ `${item._id}_${index}` } data-link={ helper.getViewLink(item) } data-index={ index }
className={this.state.selected.indexOf(index) !== -1 ? style.selected : ''}
>
<td onClick={ this.itemClicked } >
<ImageIcon data={ helper.getIcon(item) } />
</td>
<td onClick={ this.itemClicked } >{ helper.getName(item) }</td>
<td onClick={ this.itemClicked } title={ helper.getDescription(item) }>{ helper.getDescription(item, true) }</td>
<td onClick={ this.itemClicked } title={helper.getCreationDate(item)}>{ helper.getCreationDate(item, true) }</td>
<td onClick={ this.itemClicked } title={helper.getUpdateDate(item)}>{ helper.getUpdateDate(item, true) }</td>
<td>
<IconActionList actions={ helper.getActions(item) } onAction={ this.lineAction } />
</td>
</tr>
)}
</tbody>
</table>);
} else if (this.props.placeholder) {
content = this.props.placeholder;
}

return (
<div className={ style.container }>
Expand All @@ -122,36 +160,7 @@ export default React.createClass({
onAction={ this.toolbarAction }
filter
/>
<table className={ style.table }>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Description</th>
<th>Created</th>
<th>Updated</th>
<th></th>
</tr>
</thead>
<tbody>
{ filteredList.map((item, index) =>
<tr key={ `${item._id}_${index}` } data-link={ helper.getViewLink(item) } data-index={ index }
className={this.state.selected.indexOf(index) !== -1 ? style.selected : ''}
>
<td onClick={ this.itemClicked } >
<ImageIcon data={ helper.getIcon(item) } />
</td>
<td onClick={ this.itemClicked } >{ helper.getName(item) }</td>
<td onClick={ this.itemClicked } title={ helper.getDescription(item) }>{ helper.getDescription(item, true) }</td>
<td onClick={ this.itemClicked } title={helper.getCreationDate(item)}>{ helper.getCreationDate(item, true) }</td>
<td onClick={ this.itemClicked } title={helper.getUpdateDate(item)}>{ helper.getUpdateDate(item, true) }</td>
<td>
<IconActionList actions={ helper.getActions(item) } onAction={ this.lineAction } />
</td>
</tr>
)}
</tbody>
</table>
{ content }
</div>);
},
});
4 changes: 4 additions & 0 deletions style/Layout.mcss
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
width: 100%;
}

.halfHeight {
height: 50vh;
}

.horizontalPadding {
padding-left: 10px;
padding-right: 10px;
Expand Down
6 changes: 6 additions & 0 deletions style/Theme.mcss
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@
composes: fa-pencil from 'font-awesome/css/font-awesome.css';
}

.addIcon {
composes: fa from 'font-awesome/css/font-awesome.css';
composes: fa-fw from 'font-awesome/css/font-awesome.css';
composes: fa-plus from 'font-awesome/css/font-awesome.css';
}

/* Simulation icons */
.simulationEditIcon {
composes: fa from 'font-awesome/css/font-awesome.css';
Expand Down
1 change: 0 additions & 1 deletion style/Toolbar.mcss
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
align-items: center;
}


.actionButton {
composes: isClickable from 'HPCCloudStyle/States.mcss';
padding: 7px 17px;
Expand Down

0 comments on commit bba871f

Please sign in to comment.