2016-10-17 17:19:32 +00:00
|
|
|
import '../css/ProjectListItem.scss';
|
2016-10-11 17:42:17 +00:00
|
|
|
import React from 'react';
|
2016-10-17 17:19:32 +00:00
|
|
|
import update from 'react-addons-update';
|
2016-10-25 14:47:49 +00:00
|
|
|
import TaskList from './TaskList';
|
2016-10-18 15:25:14 +00:00
|
|
|
import EditTaskPanel from './EditTaskPanel';
|
2016-10-17 17:19:32 +00:00
|
|
|
import UploadProgressBar from './UploadProgressBar';
|
2016-10-12 22:18:37 +00:00
|
|
|
import Dropzone from '../vendor/dropzone';
|
2016-10-13 20:28:32 +00:00
|
|
|
import csrf from '../django/csrf';
|
2016-10-12 22:18:37 +00:00
|
|
|
import $ from 'jquery';
|
2016-10-11 17:42:17 +00:00
|
|
|
|
|
|
|
class ProjectListItem extends React.Component {
|
2016-10-11 20:37:00 +00:00
|
|
|
constructor(props){
|
|
|
|
super(props);
|
|
|
|
|
2016-10-11 17:42:17 +00:00
|
|
|
this.state = {
|
2016-10-25 14:47:49 +00:00
|
|
|
showTaskList: false,
|
2016-10-22 15:23:37 +00:00
|
|
|
updatingTask: false,
|
2016-10-21 14:42:46 +00:00
|
|
|
upload: this.getDefaultUploadState()
|
2016-10-11 17:42:17 +00:00
|
|
|
};
|
|
|
|
|
2016-10-25 14:47:49 +00:00
|
|
|
this.toggleTaskList = this.toggleTaskList.bind(this);
|
2016-10-17 17:19:32 +00:00
|
|
|
this.handleUpload = this.handleUpload.bind(this);
|
2016-10-18 15:25:14 +00:00
|
|
|
this.closeUploadError = this.closeUploadError.bind(this);
|
|
|
|
this.cancelUpload = this.cancelUpload.bind(this);
|
2016-10-21 14:42:46 +00:00
|
|
|
this.handleTaskSaved = this.handleTaskSaved.bind(this);
|
2016-10-11 17:42:17 +00:00
|
|
|
}
|
|
|
|
|
2016-10-22 15:23:37 +00:00
|
|
|
componentWillUnmount(){
|
|
|
|
if (this.updateTaskRequest) this.updateTaskRequest.abort();
|
|
|
|
}
|
|
|
|
|
2016-10-17 17:19:32 +00:00
|
|
|
getDefaultUploadState(){
|
|
|
|
return {
|
|
|
|
uploading: false,
|
2016-10-18 15:25:14 +00:00
|
|
|
showEditTask: false,
|
|
|
|
error: "",
|
2016-10-17 17:19:32 +00:00
|
|
|
progress: 0,
|
|
|
|
totalCount: 0,
|
|
|
|
totalBytes: 0,
|
2016-10-21 14:42:46 +00:00
|
|
|
totalBytesSent: 0,
|
2016-10-22 15:23:37 +00:00
|
|
|
savedTaskInfo: false,
|
2016-10-21 14:42:46 +00:00
|
|
|
taskId: null
|
2016-10-17 17:19:32 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
resetUploadState(){
|
|
|
|
this.setUploadState(this.getDefaultUploadState());
|
|
|
|
}
|
|
|
|
|
|
|
|
setUploadState(props){
|
|
|
|
this.setState(update(this.state, {
|
|
|
|
upload: {
|
|
|
|
$merge: props
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount(){
|
|
|
|
Dropzone.autoDiscover = false;
|
2016-10-12 22:18:37 +00:00
|
|
|
|
2016-10-18 15:25:14 +00:00
|
|
|
this.dz = new Dropzone(this.dropzone, {
|
2016-10-17 17:19:32 +00:00
|
|
|
paramName: "images",
|
|
|
|
url : `/api/projects/${this.props.data.id}/tasks/`,
|
|
|
|
parallelUploads: 9999999,
|
|
|
|
uploadMultiple: true,
|
|
|
|
acceptedFiles: "image/*",
|
|
|
|
autoProcessQueue: true,
|
|
|
|
createImageThumbnails: false,
|
|
|
|
clickable: this.uploadButton,
|
|
|
|
|
|
|
|
headers: {
|
|
|
|
[csrf.header]: csrf.token
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-10-18 15:25:14 +00:00
|
|
|
this.dz.on("totaluploadprogress", (progress, totalBytes, totalBytesSent) => {
|
2016-11-04 18:19:18 +00:00
|
|
|
this.setUploadState({
|
|
|
|
progress, totalBytes, totalBytesSent
|
|
|
|
});
|
2016-10-17 17:19:32 +00:00
|
|
|
})
|
|
|
|
.on("addedfile", () => {
|
|
|
|
this.setUploadState({
|
|
|
|
totalCount: this.state.upload.totalCount + 1
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.on("processingmultiple", () => {
|
|
|
|
this.setUploadState({
|
2016-10-18 15:25:14 +00:00
|
|
|
uploading: true,
|
|
|
|
showEditTask: true
|
2016-10-17 17:19:32 +00:00
|
|
|
})
|
|
|
|
})
|
2016-10-18 15:25:14 +00:00
|
|
|
.on("completemultiple", (files) => {
|
|
|
|
// Check
|
2016-10-21 14:42:46 +00:00
|
|
|
let success = files.length > 0 && files.filter(file => file.status !== "success").length === 0;
|
2016-10-18 15:25:14 +00:00
|
|
|
|
2016-10-21 14:42:46 +00:00
|
|
|
// All files have uploaded!
|
2016-10-18 15:25:14 +00:00
|
|
|
if (success){
|
|
|
|
this.setUploadState({uploading: false});
|
2016-10-21 14:42:46 +00:00
|
|
|
|
|
|
|
try{
|
|
|
|
let response = JSON.parse(files[0].xhr.response);
|
|
|
|
if (!response.id) throw new Error(`Expected id field, but none given (${response})`);
|
|
|
|
|
|
|
|
let taskId = response.id;
|
|
|
|
this.setUploadState({taskId});
|
|
|
|
|
|
|
|
// Update task information (if the user has completed this step)
|
2016-10-22 15:23:37 +00:00
|
|
|
if (this.state.upload.savedTaskInfo){
|
|
|
|
this.updateTaskInfo(taskId, this.editTaskPanel.getTaskInfo());
|
2016-10-21 14:42:46 +00:00
|
|
|
}else{
|
|
|
|
// Need to wait for user to confirm task options
|
|
|
|
}
|
|
|
|
}catch(e){
|
|
|
|
this.setUploadState({error: `Invalid response from server: ${e.message}`})
|
|
|
|
}
|
|
|
|
|
2016-10-18 15:25:14 +00:00
|
|
|
}else{
|
|
|
|
this.setUploadState({
|
|
|
|
uploading: false,
|
2016-10-21 14:42:46 +00:00
|
|
|
error: "Could not upload all files. An error occured. Please try again."
|
2016-10-18 15:25:14 +00:00
|
|
|
});
|
|
|
|
}
|
2016-10-17 17:19:32 +00:00
|
|
|
})
|
|
|
|
.on("reset", () => {
|
|
|
|
this.resetUploadState();
|
2016-10-12 22:18:37 +00:00
|
|
|
});
|
2016-10-17 17:19:32 +00:00
|
|
|
}
|
2016-10-12 22:18:37 +00:00
|
|
|
|
2016-10-22 15:23:37 +00:00
|
|
|
updateTaskInfo(taskId, taskInfo){
|
|
|
|
if (!taskId) throw new Error("taskId is not set");
|
|
|
|
if (!taskInfo) throw new Error("taskId is not set");
|
|
|
|
|
|
|
|
this.setUploadState({showEditTask: false});
|
|
|
|
this.setState({updatingTask: true});
|
|
|
|
|
2016-10-24 18:14:35 +00:00
|
|
|
this.updateTaskRequest =
|
|
|
|
$.ajax({
|
2016-10-22 15:23:37 +00:00
|
|
|
url: `/api/projects/${this.props.data.id}/tasks/${this.state.upload.taskId}/`,
|
|
|
|
contentType: 'application/json',
|
|
|
|
data: JSON.stringify({
|
|
|
|
name: taskInfo.name,
|
|
|
|
options: taskInfo.options,
|
|
|
|
processing_node: taskInfo.selectedNode.id
|
|
|
|
}),
|
|
|
|
dataType: 'json',
|
|
|
|
type: 'PATCH'
|
|
|
|
}).done(() => {
|
2016-10-25 14:47:49 +00:00
|
|
|
if (this.state.showTaskList){
|
|
|
|
this.taskList.refresh();
|
2016-10-22 15:23:37 +00:00
|
|
|
}else{
|
2016-10-25 14:47:49 +00:00
|
|
|
this.setState({showTaskList: true});
|
2016-10-22 15:23:37 +00:00
|
|
|
}
|
|
|
|
}).fail(() => {
|
|
|
|
this.setUploadState({error: "Could not update task information. Plese try again."});
|
|
|
|
}).always(() => {
|
|
|
|
this.setState({updatingTask: false});
|
|
|
|
});
|
2016-10-21 14:42:46 +00:00
|
|
|
}
|
|
|
|
|
2016-10-17 17:19:32 +00:00
|
|
|
setRef(prop){
|
|
|
|
return (domNode) => {
|
|
|
|
if (domNode != null) this[prop] = domNode;
|
2016-10-12 22:18:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-25 14:47:49 +00:00
|
|
|
toggleTaskList(){
|
2016-10-11 20:37:00 +00:00
|
|
|
this.setState({
|
2016-10-25 14:47:49 +00:00
|
|
|
showTaskList: !this.state.showTaskList
|
2016-10-11 20:37:00 +00:00
|
|
|
});
|
2016-10-11 17:42:17 +00:00
|
|
|
}
|
|
|
|
|
2016-10-18 15:25:14 +00:00
|
|
|
closeUploadError(){
|
|
|
|
this.setUploadState({error: ""});
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelUpload(e){
|
|
|
|
this.dz.removeAllFiles(true);
|
|
|
|
}
|
|
|
|
|
2016-10-17 17:19:32 +00:00
|
|
|
handleUpload(){
|
|
|
|
this.resetUploadState();
|
|
|
|
}
|
|
|
|
|
2016-10-21 14:42:46 +00:00
|
|
|
handleTaskSaved(taskInfo){
|
2016-10-22 15:23:37 +00:00
|
|
|
this.setUploadState({savedTaskInfo: true});
|
2016-10-18 15:25:14 +00:00
|
|
|
|
2016-10-21 14:42:46 +00:00
|
|
|
// Has the upload finished?
|
|
|
|
if (!this.state.upload.uploading && this.state.upload.taskId !== null){
|
2016-10-22 15:23:37 +00:00
|
|
|
this.updateTaskInfo(this.state.upload.taskId, taskInfo);
|
2016-10-21 14:42:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2016-10-11 17:42:17 +00:00
|
|
|
return (
|
2016-10-11 20:37:00 +00:00
|
|
|
<li className="project-list-item list-group-item"
|
|
|
|
href="javascript:void(0);">
|
2016-10-18 15:25:14 +00:00
|
|
|
<div className="row no-margin">
|
|
|
|
<div className="btn-group pull-right">
|
|
|
|
<button type="button"
|
|
|
|
className={"btn btn-primary btn-sm " + (this.state.upload.uploading ? "hide" : "")}
|
|
|
|
onClick={this.handleUpload}
|
|
|
|
ref={this.setRef("uploadButton")}>
|
|
|
|
<i className="glyphicon glyphicon-upload"></i>
|
|
|
|
Upload Images
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<button disabled={this.state.upload.error !== ""}
|
|
|
|
type="button"
|
|
|
|
className={"btn btn-primary btn-sm " + (!this.state.upload.uploading ? "hide" : "")}
|
|
|
|
onClick={this.cancelUpload}>
|
|
|
|
<i className="glyphicon glyphicon-remove-circle"></i>
|
|
|
|
Cancel Upload
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<button type="button" className="btn btn-default btn-sm">
|
|
|
|
<i className="fa fa-globe"></i> Map View
|
|
|
|
</button>
|
|
|
|
<button type="button" className="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
|
|
|
|
<span className="caret"></span>
|
|
|
|
</button>
|
|
|
|
<ul className="dropdown-menu">
|
|
|
|
<li><a href="javascript:alert('TODO!');"><i className="fa fa-cube"></i> 3D View</a></li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
2016-10-11 20:37:00 +00:00
|
|
|
|
2016-10-25 14:47:49 +00:00
|
|
|
<span className="project-name">
|
2016-10-18 15:25:14 +00:00
|
|
|
{this.props.data.name}
|
2016-10-25 14:47:49 +00:00
|
|
|
</span>
|
|
|
|
<div className="project-description">
|
|
|
|
{this.props.data.description}
|
|
|
|
</div>
|
|
|
|
<div className="row project-links">
|
|
|
|
<i className='fa fa-tasks'>
|
|
|
|
</i> <a href="javascript:void(0);" onClick={this.toggleTaskList}>
|
|
|
|
{(this.state.showTaskList ? 'Hide' : 'Show')} Tasks
|
|
|
|
</a>
|
|
|
|
</div>
|
2016-10-18 15:25:14 +00:00
|
|
|
</div>
|
|
|
|
<div className="row">
|
|
|
|
<div className="dropzone" ref={this.setRef("dropzone")}>
|
|
|
|
<div className="dz-default dz-message text-center">
|
|
|
|
</div>
|
2016-10-17 17:19:32 +00:00
|
|
|
</div>
|
2016-10-11 17:42:17 +00:00
|
|
|
|
2016-10-21 14:42:46 +00:00
|
|
|
{this.state.upload.showEditTask ? <UploadProgressBar {...this.state.upload}/> : ""}
|
2016-10-18 15:25:14 +00:00
|
|
|
|
|
|
|
{this.state.upload.error !== "" ?
|
|
|
|
<div className="alert alert-warning alert-dismissible">
|
|
|
|
<button type="button" className="close" aria-label="Close" onClick={this.closeUploadError}><span aria-hidden="true">×</span></button>
|
|
|
|
{this.state.upload.error}
|
2016-10-12 22:18:37 +00:00
|
|
|
</div>
|
2016-10-18 15:25:14 +00:00
|
|
|
: ""}
|
2016-10-12 22:18:37 +00:00
|
|
|
|
2016-10-22 15:23:37 +00:00
|
|
|
{this.state.upload.showEditTask ?
|
|
|
|
<EditTaskPanel
|
|
|
|
uploading={this.state.upload.uploading}
|
|
|
|
onSave={this.handleTaskSaved}
|
|
|
|
ref={this.setRef("editTaskPanel")}
|
2016-10-21 14:42:46 +00:00
|
|
|
/>
|
2016-10-22 15:23:37 +00:00
|
|
|
: ""}
|
|
|
|
|
|
|
|
{this.state.updatingTask ?
|
|
|
|
<span>Updating task information... <i className="fa fa-refresh fa-spin fa-fw"></i></span>
|
|
|
|
: ""}
|
2016-10-17 17:19:32 +00:00
|
|
|
|
2016-11-04 18:19:18 +00:00
|
|
|
{this.state.showTaskList ? <TaskList ref={this.setRef("taskList")} source={`/api/projects/${this.props.data.id}/tasks/?ordering=-created_at`}/> : ""}
|
2016-11-02 22:32:24 +00:00
|
|
|
|
2016-10-18 15:25:14 +00:00
|
|
|
</div>
|
2016-10-11 20:37:00 +00:00
|
|
|
</li>
|
2016-10-11 17:42:17 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default ProjectListItem;
|