Refactor HostsTable for fun and SPEED

pull/635/head
Will Piers 2016-12-01 16:31:21 -08:00
parent e06f1c5cf4
commit 9dc8e02b1d
1 changed files with 89 additions and 55 deletions

View File

@ -19,24 +19,14 @@ const HostsTable = React.createClass({
getInitialState() {
return {
searchTerm: '',
filteredHosts: this.props.hosts,
sortDirection: null,
sortKey: null,
};
},
componentWillReceiveProps(newProps) {
this.filterHosts(newProps.hosts, this.state.searchTerm);
},
filterHosts(allHosts, searchTerm) {
const hosts = allHosts.filter((h) => {
let apps = null;
if (h.apps) {
apps = h.apps.join(', ');
} else {
apps = '';
}
filter(allHosts, searchTerm) {
return allHosts.filter((h) => {
const apps = h.apps ? h.apps.join(', ') : '';
// search each tag for the presence of the search term
let tagResult = false;
if (h.tags) {
@ -52,17 +42,6 @@ const HostsTable = React.createClass({
tagResult
);
});
this.setState({searchTerm, filteredHosts: hosts});
},
changeSort(key) {
// if we're using the key, reverse order; otherwise, set it with ascending
if (this.state.sortKey === key) {
const reverseDirection = (this.state.sortDirection === 'asc' ? 'desc' : 'asc');
this.setState({sortDirection: reverseDirection});
} else {
this.setState({sortKey: key, sortDirection: 'asc'});
}
},
sort(hosts, key, direction) {
@ -76,6 +55,20 @@ const HostsTable = React.createClass({
}
},
updateSearchTerm(term) {
this.setState({searchTerm: term});
},
updateSort(key) {
// if we're using the key, reverse order; otherwise, set it with ascending
if (this.state.sortKey === key) {
const reverseDirection = (this.state.sortDirection === 'asc' ? 'desc' : 'asc');
this.setState({sortDirection: reverseDirection});
} else {
this.setState({sortKey: key, sortDirection: 'asc'});
}
},
sortableClasses(key) {
if (this.state.sortKey === key) {
if (this.state.sortDirection === 'asc') {
@ -87,52 +80,32 @@ const HostsTable = React.createClass({
},
render() {
const hosts = this.sort(this.state.filteredHosts, this.state.sortKey, this.state.sortDirection);
const hostCount = hosts.length;
const {source} = this.props;
const {searchTerm, sortKey, sortDirection} = this.state;
const {hosts, source} = this.props;
const sortedHosts = this.sort(this.filter(hosts, searchTerm), sortKey, sortDirection);
const hostCount = sortedHosts.length;
return (
<div className="panel panel-minimal">
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
<h2 className="panel-title">{hostCount ? `${hostCount} Hosts` : ''}</h2>
<SearchBar onSearch={_.wrap(this.props.hosts, this.filterHosts)} />
<SearchBar onSearch={this.updateSearchTerm} />
</div>
<div className="panel-body">
<table className="table v-center">
<thead>
<tr>
<th onClick={() => this.changeSort('name')} className={this.sortableClasses('name')}>Hostname</th>
<th onClick={() => this.updateSort('name')} className={this.sortableClasses('name')}>Hostname</th>
<th className="text-center">Status</th>
<th onClick={() => this.changeSort('cpu')} className={this.sortableClasses('cpu')}>CPU</th>
<th onClick={() => this.changeSort('load')} className={this.sortableClasses('load')}>Load</th>
<th onClick={() => this.updateSort('cpu')} className={this.sortableClasses('cpu')}>CPU</th>
<th onClick={() => this.updateSort('load')} className={this.sortableClasses('load')}>Load</th>
<th>Apps</th>
</tr>
</thead>
<tbody>
{
hosts.map(({name, cpu, load, apps = []}) => {
return (
<tr key={name}>
<td className="monotype"><Link to={`/sources/${source.id}/hosts/${name}`}>{name}</Link></td>
<td className="text-center"><div className="table-dot dot-success"></div></td>
<td className="monotype">{isNaN(cpu) ? 'N/A' : `${cpu.toFixed(2)}%`}</td>
<td className="monotype">{isNaN(load) ? 'N/A' : `${load.toFixed(2)}`}</td>
<td className="monotype">
{apps.map((app, index) => {
return (
<span key={app}>
<Link
style={{marginLeft: "2px"}}
to={{pathname: `/sources/${source.id}/hosts/${name}`, query: {app}}}>
{app}
</Link>
{index === apps.length - 1 ? null : ', '}
</span>
);
})}
</td>
</tr>
);
sortedHosts.map((h) => {
return <HostRow key={h.name} host={h} source={source} />;
})
}
</tbody>
@ -143,13 +116,74 @@ const HostsTable = React.createClass({
},
});
const HostRow = React.createClass({
propTypes: {
source: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}).isRequired,
host: PropTypes.shape({
name: PropTypes.string,
cpu: PropTypes.number,
load: PropTypes.number,
apps: PropTypes.arrayOf(PropTypes.string.isRequired),
}),
},
shouldComponentUpdate(nextProps) {
return this.props.host !== nextProps.host;
},
render() {
const {host, source} = this.props;
const {name, cpu, load, apps = []} = host;
return (
<tr>
<td className="monotype"><Link to={`/sources/${source.id}/hosts/${name}`}>{name}</Link></td>
<td className="text-center"><div className="table-dot dot-success"></div></td>
<td className="monotype">{isNaN(cpu) ? 'N/A' : `${cpu.toFixed(2)}%`}</td>
<td className="monotype">{isNaN(load) ? 'N/A' : `${load.toFixed(2)}`}</td>
<td className="monotype">
{apps.map((app, index) => {
return (
<span key={app}>
<Link
style={{marginLeft: "2px"}}
to={{pathname: `/sources/${source.id}/hosts/${name}`, query: {app}}}>
{app}
</Link>
{index === apps.length - 1 ? null : ', '}
</span>
);
})}
</td>
</tr>
);
},
});
const SearchBar = React.createClass({
propTypes: {
onSearch: PropTypes.func.isRequired,
},
getInitialState() {
return {
searchTerm: '',
};
},
componentWillMount() {
const waitPeriod = 300;
this.handleSearch = _.debounce(this.handleSearch, waitPeriod);
},
handleSearch() {
this.props.onSearch(this.state.searchTerm);
},
handleChange() {
this.props.onSearch(this.refs.searchInput.value);
this.setState({searchTerm: this.refs.searchInput.value}, this.handleSearch);
},
render() {