Processing node options rendering on react, processing node selection from API

pull/34/head
Piero Toffanin 2016-10-19 14:00:03 -04:00
rodzic 75c83ff03b
commit a93fd5ae46
5 zmienionych plików z 255 dodań i 32 usunięć

Wyświetl plik

@ -1,5 +1,6 @@
import '../css/EditTaskPanel.scss';
import React from 'react';
import ProcessingNodeOption from './ProcessingNodeOption';
class EditTaskPanel extends React.Component {
constructor(){
@ -9,66 +10,198 @@ class EditTaskPanel extends React.Component {
this.state = {
name: "",
advancedOptions: false
advancedOptions: false,
loadedProcessingNodes: false,
selectedNode: null,
processingNodes: [],
editing: true
};
// Refs to ProcessingNodeOption components
this.options = {};
this.handleNameChange = this.handleNameChange.bind(this);
this.setAdvancedOptions = this.setAdvancedOptions.bind(this);
this.handleSelectNode = this.handleSelectNode.bind(this);
this.setOptionRef = this.setOptionRef.bind(this);
this.save = this.save.bind(this);
this.edit = this.edit.bind(this);
}
componentDidMount(){
// Load projects from API
const loadProcessingNodes = () => {
function failed(){
// Try again
setTimeout(loadProcessingNodes, 1000);
}
this.nodesRequest = $.getJSON("/api/processingnodes/", json => {
if (Array.isArray(json)){
let nodes = json.map(node => {
return {
id: node.id,
key: node.id,
label: `${node.hostname}:${node.port} (queue: ${node.queue_count})`,
options: node.available_options
};
});
// Find a node with lowest queue count
let minQueueCount = Math.min(...json.map(node => node.queue_count));
let minQueueCountnodes = json.filter(node => node.queue_count === minQueueCount);
// Choose at random
let autoNode = minQueueCountnodes[~~(Math.random() * minQueueCountnodes.length)];
nodes.unshift({
id: autoNode.id,
key: "auto",
label: "Auto",
options: autoNode.available_options
});
this.setState({
selectedNode: nodes[0],
processingNodes: nodes,
loadedProcessingNodes: true
});
}else{
console.error("Got invalid json response for processing nodes", json);
failed();
}
})
.fail((jqXHR, textStatus, errorThrown) => {
// I don't expect this to fail, unless it's a development error or connection error.
// in which case we don't need to notify the user directly.
console.error("Error retrieving processing nodes", jqXHR, textStatus);
failed();
});
}
loadProcessingNodes();
}
componentWillUnmount(){
this.nodesRequest.abort();
}
handleNameChange(e){
this.setState({name: e.target.value});
}
handleSelectNode(e){
this.options = {};
this.setState({selectedNode: this.state.processingNodes.find(node => node.key == e.target.value)});
}
setAdvancedOptions(flag){
return () => {
this.setState({advancedOptions: flag});
};
}
save(){
setOptionRef(optionName){
return (component) => {
if (component) this.options[optionName] = component;
}
}
getOptions(){
return Object.values(this.options)
.map(option => {
return {
name: option.props.name,
value: option.getValue()
};
})
.filter(option => option.value !== undefined);
}
save(e){
e.preventDefault();
// console.log(this.getOptions());
// console.log(this.state.selectedNode);
// console.log(this.state.name || this.namePlaceholder);
this.setState({editing: false});
}
edit(e){
e.preventDefault();
this.setState({editing: true});
}
render() {
return (
<div className="edit-task-panel">
<form className="form-horizontal">
<p>Your images are being uploaded. In the meanwhile, check these additional options:</p>
<div className="form-group">
<label className="col-sm-2 control-label">Name</label>
<div className="col-sm-10">
<input type="text" onChange={this.handleNameChange} className="form-control" placeholder={this.namePlaceholder} />
if (this.state.editing){
let processingNodesOptions = "";
if (this.state.loadedProcessingNodes){
processingNodesOptions = (
<div>
<div className="form-group">
<label className="col-sm-2 control-label">Processing Node</label>
<div className="col-sm-10">
<select className="form-control" value={this.state.selectedNode.key} onChange={this.handleSelectNode}>
{this.state.processingNodes.map(node =>
<option value={node.key} key={node.key}>{node.label}</option>
)}
</select>
</div>
</div>
</div>
<div className="form-group">
<label className="col-sm-2 control-label">Processing Node</label>
<div className="form-group">
<label className="col-sm-2 control-label">Options</label>
<div className="col-sm-10">
<select className="form-control">
<option>Auto</option>
<option>1</option>
<option>2</option>
</select>
<div className="btn-group" role="group">
<button type="button" className={"btn " + (!this.state.advancedOptions ? "btn-default" : "btn-secondary")} onClick={this.setAdvancedOptions(false)}>Default</button>
<button type="button" className={"btn " + (this.state.advancedOptions ? "btn-default" : "btn-secondary")} onClick={this.setAdvancedOptions(true)}>Advanced</button>
</div>
</div>
</div>
<div className="form-group">
<label className="col-sm-2 control-label">Options</label>
<div className="col-sm-10">
<div className="btn-group" role="group">
<button type="button" className={"btn " + (!this.state.advancedOptions ? "btn-default" : "btn-secondary")} onClick={this.setAdvancedOptions(false)}>Default</button>
<button type="button" className={"btn " + (this.state.advancedOptions ? "btn-default" : "btn-secondary")} onClick={this.setAdvancedOptions(true)}>Advanced</button>
</div>
<div className={"form-group " + (!this.state.advancedOptions ? "hide" : "")}>
<div className="col-sm-offset-2 col-sm-10">
{this.state.selectedNode.options.map(option =>
<ProcessingNodeOption {...option}
key={option.name}
ref={this.setOptionRef(option.name)} />
)}
</div>
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10 text-right">
<button type="submit" className="btn btn-default" onClick={this.save}>Save</button>
);
}else{
processingNodesOptions = (<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">Loading processing nodes... <i className="fa fa-refresh fa-spin fa-fw"></i></div>
</div>);
}
return (
<div className="edit-task-panel">
<form className="form-horizontal">
<p>Your images are being uploaded. In the meanwhile, check these additional options:</p>
<div className="form-group">
<label className="col-sm-2 control-label">Name</label>
<div className="col-sm-10">
<input type="text" onChange={this.handleNameChange} className="form-control" placeholder={this.namePlaceholder} />
</div>
</div>
{processingNodesOptions}
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10 text-right">
<button type="submit" className="btn btn-primary" onClick={this.save}><i className="glyphicon glyphicon-saved"></i> Save</button>
</div>
</div>
</form>
</div>
);
}else{
return (
<div className="edit-task-panel">
<div className="pull-right">
<button type="submit" className="btn btn-primary btn-sm glyphicon glyphicon-pencil" onClick={this.edit}></button>
</div>
<p className="header"><strong>Thank you!</strong> Please wait for the upload to complete.</p>
</div>
</form>
</div>
);
);
}
}
}

Wyświetl plik

@ -0,0 +1,72 @@
import React from 'react';
import '../css/ProcessingNodeOption.scss';
import $ from 'jquery';
class ProcessingNodeOption extends React.Component {
constructor(props){
super();
this.state = {
value: ""
};
this.resetToDefault = this.resetToDefault.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
}
getValue(){
return this.state.value !== "" ? this.state.value : undefined;
}
setTooltips(domNode){
if (domNode !== null) $(domNode).children('[data-toggle="tooltip"]').tooltip();
}
resetToDefault(e){
this.setState({value: ""});
e.preventDefault();
}
handleInputChange(e){
this.setState({value: e.target.value});
}
handleCheckboxChange(e){
this.setState({value: this.state.value === "" ? true : ""});
}
render() {
let inputControl = "";
if (this.props.type !== 'bool'){
inputControl = (
<input type="text"
className="form-control"
placeholder={this.props.value}
value={this.state.value}
onChange={this.handleInputChange} />
);
}else{
inputControl = (
<div className="checkbox">
<label>
<input type="checkbox"
checked={this.state.value !== ""}
onChange={this.handleCheckboxChange} /> Enable
</label>
</div>
);
}
return (
<div className="processing-node-option form-inline form-group form-horizontal" ref={this.setTooltips}>
<label>{this.props.name} {(this.props.domain ? `(${this.props.domain})` : "")}</label><br/>
{inputControl}
<button type="submit" className="btn glyphicon glyphicon-info-sign btn-info" data-toggle="tooltip" data-placement="top" title={this.props.help} onClick={e => e.preventDefault()}></button>
<button type="submit" className="btn glyphicon glyphicon glyphicon-repeat btn-default" data-toggle="tooltip" data-placement="top" title="Reset to default" onClick={this.resetToDefault}></button>
</div>
);
}
}
export default ProcessingNodeOption;

Wyświetl plik

@ -43,7 +43,7 @@ class ProjectList extends React.Component {
render() {
if (this.state.loading){
return (<div>Loading projects...</div>);
return (<div>Loading projects... <i className="fa fa-refresh fa-spin fa-fw"></i></div>);
}
else if (this.state.projects){
return (<ul className="list-group">

Wyświetl plik

@ -2,4 +2,8 @@
background-color: #ecf0f1;
padding: 10px;
margin-top: 10px;
p.header{
margin: 8px;
}
}

Wyświetl plik

@ -0,0 +1,14 @@
.processing-node-option{
.checkbox{
margin-right: 143px;
}
button{
margin-left: 4px;
}
&.form-horizontal.form-group{
margin-left: 0;
margin-right: 0;
}
}