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-11-28 20:18:53 +00:00
|
|
|
import update from 'immutability-helper';
|
2016-10-25 14:47:49 +00:00
|
|
|
import TaskList from './TaskList';
|
2017-07-18 20:07:53 +00:00
|
|
|
import NewTaskPanel from './NewTaskPanel';
|
2019-02-20 21:42:20 +00:00
|
|
|
import ImportTaskPanel from './ImportTaskPanel';
|
2016-10-17 17:19:32 +00:00
|
|
|
import UploadProgressBar from './UploadProgressBar';
|
2017-11-22 19:53:29 +00:00
|
|
|
import ProgressBar from './ProgressBar';
|
2016-11-14 15:58:00 +00:00
|
|
|
import ErrorMessage from './ErrorMessage';
|
2016-11-14 21:32:05 +00:00
|
|
|
import EditProjectDialog from './EditProjectDialog';
|
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';
|
2017-04-13 21:03:42 +00:00
|
|
|
import HistoryNav from '../classes/HistoryNav';
|
2017-09-06 15:47:04 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2018-02-19 20:50:26 +00:00
|
|
|
import ResizeModes from '../classes/ResizeModes';
|
2018-02-20 16:11:24 +00:00
|
|
|
import Gcp from '../classes/Gcp';
|
2016-10-12 22:18:37 +00:00
|
|
|
import $ from 'jquery';
|
2016-10-11 17:42:17 +00:00
|
|
|
|
|
|
|
class ProjectListItem extends React.Component {
|
2017-09-06 15:47:04 +00:00
|
|
|
static propTypes = {
|
|
|
|
history: PropTypes.object.isRequired,
|
|
|
|
data: PropTypes.object.isRequired, // project json
|
|
|
|
onDelete: PropTypes.func
|
|
|
|
}
|
|
|
|
|
2016-10-11 20:37:00 +00:00
|
|
|
constructor(props){
|
|
|
|
super(props);
|
|
|
|
|
2017-04-13 21:03:42 +00:00
|
|
|
this.historyNav = new HistoryNav(props.history);
|
|
|
|
|
2016-10-11 17:42:17 +00:00
|
|
|
this.state = {
|
2017-04-13 21:03:42 +00:00
|
|
|
showTaskList: this.historyNav.isValueInQSList("project_task_open", props.data.id),
|
2016-11-14 15:58:00 +00:00
|
|
|
upload: this.getDefaultUploadState(),
|
2016-11-14 21:32:05 +00:00
|
|
|
error: "",
|
2016-11-15 16:51:19 +00:00
|
|
|
data: props.data,
|
2019-02-20 21:42:20 +00:00
|
|
|
refreshing: false,
|
|
|
|
importing: false
|
2016-10-11 17:42:17 +00:00
|
|
|
};
|
|
|
|
|
2016-10-25 14:47:49 +00:00
|
|
|
this.toggleTaskList = this.toggleTaskList.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-11-09 21:13:43 +00:00
|
|
|
this.viewMap = this.viewMap.bind(this);
|
2016-11-14 15:58:00 +00:00
|
|
|
this.handleDelete = this.handleDelete.bind(this);
|
2016-11-14 21:32:05 +00:00
|
|
|
this.handleEditProject = this.handleEditProject.bind(this);
|
|
|
|
this.updateProject = this.updateProject.bind(this);
|
|
|
|
this.taskDeleted = this.taskDeleted.bind(this);
|
2017-06-15 19:47:00 +00:00
|
|
|
this.hasPermission = this.hasPermission.bind(this);
|
2016-10-11 17:42:17 +00:00
|
|
|
}
|
|
|
|
|
2016-11-15 16:51:19 +00:00
|
|
|
refresh(){
|
|
|
|
// Update project information based on server
|
|
|
|
this.setState({refreshing: true});
|
|
|
|
|
|
|
|
this.refreshRequest =
|
|
|
|
$.getJSON(`/api/projects/${this.state.data.id}/`)
|
|
|
|
.done((json) => {
|
|
|
|
this.setState({data: json});
|
|
|
|
})
|
|
|
|
.fail((_, __, e) => {
|
|
|
|
this.setState({error: e.message});
|
|
|
|
})
|
|
|
|
.always(() => {
|
|
|
|
this.setState({refreshing: false});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-22 15:23:37 +00:00
|
|
|
componentWillUnmount(){
|
2016-11-14 15:58:00 +00:00
|
|
|
if (this.deleteProjectRequest) this.deleteProjectRequest.abort();
|
2016-11-15 16:51:19 +00:00
|
|
|
if (this.refreshRequest) this.refreshRequest.abort();
|
2016-10-22 15:23:37 +00:00
|
|
|
}
|
|
|
|
|
2016-10-17 17:19:32 +00:00
|
|
|
getDefaultUploadState(){
|
|
|
|
return {
|
|
|
|
uploading: false,
|
2017-07-18 20:07:53 +00:00
|
|
|
editing: false,
|
2017-11-22 19:53:29 +00:00
|
|
|
resizing: false,
|
|
|
|
resizedImages: 0,
|
2016-10-18 15:25:14 +00:00
|
|
|
error: "",
|
2016-10-17 17:19:32 +00:00
|
|
|
progress: 0,
|
2018-12-31 21:50:51 +00:00
|
|
|
files: [],
|
2016-10-17 17:19:32 +00:00
|
|
|
totalCount: 0,
|
|
|
|
totalBytes: 0,
|
2018-12-04 23:40:38 +00:00
|
|
|
totalBytesSent: 0,
|
|
|
|
lastUpdated: 0
|
2016-10-17 17:19:32 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
resetUploadState(){
|
|
|
|
this.setUploadState(this.getDefaultUploadState());
|
|
|
|
}
|
|
|
|
|
|
|
|
setUploadState(props){
|
|
|
|
this.setState(update(this.state, {
|
|
|
|
upload: {
|
|
|
|
$merge: props
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2017-06-15 19:47:00 +00:00
|
|
|
hasPermission(perm){
|
|
|
|
return this.state.data.permissions.indexOf(perm) !== -1;
|
|
|
|
}
|
|
|
|
|
2016-10-17 17:19:32 +00:00
|
|
|
componentDidMount(){
|
|
|
|
Dropzone.autoDiscover = false;
|
2016-10-12 22:18:37 +00:00
|
|
|
|
2017-06-15 19:47:00 +00:00
|
|
|
if (this.hasPermission("add")){
|
|
|
|
this.dz = new Dropzone(this.dropzone, {
|
|
|
|
paramName: "images",
|
|
|
|
url : `/api/projects/${this.state.data.id}/tasks/`,
|
2017-11-25 15:45:27 +00:00
|
|
|
parallelUploads: 2147483647,
|
2017-06-15 19:47:00 +00:00
|
|
|
uploadMultiple: true,
|
2018-03-31 17:08:56 +00:00
|
|
|
acceptedFiles: "image/*,text/*",
|
2017-11-20 20:52:23 +00:00
|
|
|
autoProcessQueue: false,
|
2017-06-15 19:47:00 +00:00
|
|
|
createImageThumbnails: false,
|
|
|
|
clickable: this.uploadButton,
|
2017-11-25 15:45:27 +00:00
|
|
|
chunkSize: 2147483647,
|
|
|
|
timeout: 2147483647,
|
2017-06-15 19:47:00 +00:00
|
|
|
|
|
|
|
headers: {
|
|
|
|
[csrf.header]: csrf.token
|
2018-02-20 16:11:24 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
transformFile: (file, done) => {
|
|
|
|
// Resize image?
|
|
|
|
if ((this.dz.options.resizeWidth || this.dz.options.resizeHeight) && file.type.match(/image.*/)) {
|
|
|
|
return this.dz.resizeImage(file, this.dz.options.resizeWidth, this.dz.options.resizeHeight, this.dz.options.resizeMethod, done);
|
|
|
|
// Resize GCP? This should always be executed last (we sort in transformstart)
|
|
|
|
} else if (this.dz.options.resizeWidth && file.type.match(/text.*/)){
|
|
|
|
// Read GCP content
|
|
|
|
const fileReader = new FileReader();
|
|
|
|
fileReader.onload = (e) => {
|
|
|
|
const originalGcp = new Gcp(e.target.result);
|
|
|
|
const resizedGcp = originalGcp.resize(this.dz._resizeMap);
|
|
|
|
// Create new GCP file
|
|
|
|
let gcp = new Blob([resizedGcp.toString()], {type: "text/plain"});
|
|
|
|
gcp.lastModifiedDate = file.lastModifiedDate;
|
|
|
|
gcp.lastModified = file.lastModified;
|
|
|
|
gcp.name = file.name;
|
|
|
|
gcp.previewElement = file.previewElement;
|
|
|
|
gcp.previewTemplate = file.previewTemplate;
|
|
|
|
gcp.processing = file.processing;
|
|
|
|
gcp.status = file.status;
|
|
|
|
gcp.upload = file.upload;
|
|
|
|
gcp.upload.total = gcp.size; // not a typo
|
|
|
|
gcp.webkitRelativePath = file.webkitRelativePath;
|
|
|
|
done(gcp);
|
|
|
|
};
|
|
|
|
fileReader.readAsText(file);
|
|
|
|
} else {
|
|
|
|
return done(file);
|
|
|
|
}
|
2017-11-22 19:53:29 +00:00
|
|
|
}
|
2017-06-15 19:47:00 +00:00
|
|
|
});
|
2016-10-21 14:42:46 +00:00
|
|
|
|
2017-06-15 19:47:00 +00:00
|
|
|
this.dz.on("totaluploadprogress", (progress, totalBytes, totalBytesSent) => {
|
2018-12-04 23:40:38 +00:00
|
|
|
// Limit updates since this gets called a lot
|
|
|
|
let now = (new Date()).getTime();
|
|
|
|
|
|
|
|
// Progress 100 is sent multiple times at the end
|
|
|
|
// this makes it so that we update the state only once.
|
|
|
|
if (progress === 100) now = now + 9999999999;
|
|
|
|
|
|
|
|
if (this.state.upload.lastUpdated + 500 < now){
|
|
|
|
this.setUploadState({
|
|
|
|
progress, totalBytes, totalBytesSent, lastUpdated: now
|
|
|
|
});
|
|
|
|
}
|
2017-06-15 19:47:00 +00:00
|
|
|
})
|
2017-11-20 20:52:23 +00:00
|
|
|
.on("addedfiles", files => {
|
2017-06-15 19:47:00 +00:00
|
|
|
this.setUploadState({
|
2017-11-22 19:53:29 +00:00
|
|
|
editing: true,
|
2018-12-31 21:50:51 +00:00
|
|
|
totalCount: this.state.upload.totalCount + files.length,
|
|
|
|
files
|
2017-06-15 19:47:00 +00:00
|
|
|
});
|
2017-11-20 20:52:23 +00:00
|
|
|
})
|
2018-02-20 16:11:24 +00:00
|
|
|
.on("transformcompleted", (file, total) => {
|
|
|
|
if (this.dz._resizeMap) this.dz._resizeMap[file.name] = this.dz._taskInfo.resizeSize / Math.max(file.width, file.height);
|
2018-12-04 23:40:38 +00:00
|
|
|
if (this.dz.options.resizeWidth) this.setUploadState({resizedImages: total});
|
2017-11-20 20:52:23 +00:00
|
|
|
})
|
2018-02-20 16:11:24 +00:00
|
|
|
.on("transformstart", (files) => {
|
|
|
|
if (this.dz.options.resizeWidth){
|
|
|
|
// Sort so that a GCP file is always last
|
|
|
|
files.sort(f => f.type.match(/text.*/) ? 1 : -1)
|
|
|
|
|
|
|
|
// Create filename --> resize ratio dict
|
|
|
|
this.dz._resizeMap = {};
|
|
|
|
}
|
|
|
|
})
|
2017-11-20 20:52:23 +00:00
|
|
|
.on("transformend", () => {
|
2017-11-22 19:53:29 +00:00
|
|
|
this.setUploadState({resizing: false, uploading: true});
|
2017-06-15 19:47:00 +00:00
|
|
|
})
|
|
|
|
.on("completemultiple", (files) => {
|
|
|
|
// Check
|
2018-09-27 02:36:15 +00:00
|
|
|
const invalidFilesCount = files.filter(file => file.status !== "success").length;
|
|
|
|
let success = files.length > 0 && invalidFilesCount === 0;
|
2017-06-15 19:47:00 +00:00
|
|
|
|
|
|
|
// All files have uploaded!
|
|
|
|
if (success){
|
|
|
|
this.setUploadState({uploading: false});
|
|
|
|
try{
|
|
|
|
let response = JSON.parse(files[0].xhr.response);
|
|
|
|
if (!response.id) throw new Error(`Expected id field, but none given (${response})`);
|
|
|
|
|
2019-02-21 19:16:48 +00:00
|
|
|
this.newTaskAdded();
|
2017-06-15 19:47:00 +00:00
|
|
|
}catch(e){
|
2017-11-22 19:53:29 +00:00
|
|
|
this.setUploadState({error: `Invalid response from server: ${e.message}`, uploading: false})
|
2017-06-15 19:47:00 +00:00
|
|
|
}
|
|
|
|
}else{
|
|
|
|
this.setUploadState({
|
2018-09-27 02:36:15 +00:00
|
|
|
totalCount: this.state.upload.totalCount - invalidFilesCount,
|
2017-06-15 19:47:00 +00:00
|
|
|
uploading: false,
|
2018-09-27 02:59:26 +00:00
|
|
|
error: `${invalidFilesCount} files cannot be uploaded. As a reminder, only images (.jpg, .png) and GCP files (.txt) can be uploaded. Try again.`
|
2017-06-15 19:47:00 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.on("reset", () => {
|
|
|
|
this.resetUploadState();
|
|
|
|
})
|
|
|
|
.on("dragenter", () => {
|
2018-01-18 18:03:02 +00:00
|
|
|
if (!this.state.upload.editing){
|
2017-11-22 19:53:29 +00:00
|
|
|
this.resetUploadState();
|
|
|
|
}
|
2017-06-15 19:47:00 +00:00
|
|
|
})
|
|
|
|
.on("sending", (file, xhr, formData) => {
|
2017-11-22 19:53:29 +00:00
|
|
|
const taskInfo = this.dz._taskInfo;
|
|
|
|
|
2017-12-21 15:01:23 +00:00
|
|
|
// Safari does not have support for has on FormData
|
|
|
|
// as of December 2017
|
|
|
|
if (!formData.has || !formData.has("name")) formData.append("name", taskInfo.name);
|
|
|
|
if (!formData.has || !formData.has("options")) formData.append("options", JSON.stringify(taskInfo.options));
|
|
|
|
if (!formData.has || !formData.has("processing_node")) formData.append("processing_node", taskInfo.selectedNode.id);
|
|
|
|
if (!formData.has || !formData.has("auto_processing_node")) formData.append("auto_processing_node", taskInfo.selectedNode.key == "auto");
|
2018-02-19 20:50:26 +00:00
|
|
|
|
|
|
|
if (taskInfo.resizeMode === ResizeModes.YES){
|
|
|
|
if (!formData.has || !formData.has("resize_to")) formData.append("resize_to", taskInfo.resizeSize);
|
|
|
|
}
|
2017-06-15 19:47:00 +00:00
|
|
|
});
|
|
|
|
}
|
2016-10-17 17:19:32 +00:00
|
|
|
}
|
2016-10-12 22:18:37 +00:00
|
|
|
|
2019-02-21 19:16:48 +00:00
|
|
|
newTaskAdded = () => {
|
|
|
|
this.setState({importing: false});
|
|
|
|
|
|
|
|
if (this.state.showTaskList){
|
|
|
|
this.taskList.refresh();
|
|
|
|
}else{
|
|
|
|
this.setState({showTaskList: true});
|
|
|
|
}
|
|
|
|
this.resetUploadState();
|
|
|
|
this.refresh();
|
|
|
|
}
|
|
|
|
|
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(){
|
2017-04-13 21:03:42 +00:00
|
|
|
const showTaskList = !this.state.showTaskList;
|
|
|
|
|
|
|
|
this.historyNav.toggleQSListItem("project_task_open", this.state.data.id, showTaskList);
|
|
|
|
|
2016-10-11 20:37:00 +00:00
|
|
|
this.setState({
|
2017-04-13 21:03:42 +00:00
|
|
|
showTaskList: 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-11-14 21:32:05 +00:00
|
|
|
taskDeleted(){
|
2016-11-15 16:51:19 +00:00
|
|
|
this.refresh();
|
2016-11-14 21:32:05 +00:00
|
|
|
}
|
|
|
|
|
2016-11-14 15:58:00 +00:00
|
|
|
handleDelete(){
|
2016-11-15 16:51:19 +00:00
|
|
|
return $.ajax({
|
|
|
|
url: `/api/projects/${this.state.data.id}/`,
|
|
|
|
type: 'DELETE'
|
|
|
|
}).done(() => {
|
|
|
|
if (this.props.onDelete) this.props.onDelete(this.state.data.id);
|
|
|
|
});
|
2016-11-14 15:58:00 +00:00
|
|
|
}
|
|
|
|
|
2016-10-21 14:42:46 +00:00
|
|
|
handleTaskSaved(taskInfo){
|
2017-11-22 19:53:29 +00:00
|
|
|
this.dz._taskInfo = taskInfo; // Allow us to access the task info from dz
|
|
|
|
|
|
|
|
// Update dropzone settings
|
2018-02-19 20:50:26 +00:00
|
|
|
if (taskInfo.resizeMode === ResizeModes.YESINBROWSER){
|
|
|
|
this.dz.options.resizeWidth = taskInfo.resizeSize;
|
2017-11-22 19:53:29 +00:00
|
|
|
this.dz.options.resizeQuality = 1.0;
|
2016-10-18 15:25:14 +00:00
|
|
|
|
2017-11-22 19:53:29 +00:00
|
|
|
this.setUploadState({resizing: true, editing: false});
|
|
|
|
}else{
|
|
|
|
this.setUploadState({uploading: true, editing: false});
|
2016-10-21 14:42:46 +00:00
|
|
|
}
|
2017-11-22 19:53:29 +00:00
|
|
|
|
2018-01-18 18:03:02 +00:00
|
|
|
setTimeout(() => {
|
|
|
|
this.dz.processQueue();
|
|
|
|
}, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
handleTaskCanceled = () => {
|
|
|
|
this.dz.removeAllFiles(true);
|
|
|
|
this.resetUploadState();
|
|
|
|
}
|
|
|
|
|
|
|
|
handleUpload = () => {
|
|
|
|
// Not a second click for adding more files?
|
|
|
|
if (!this.state.upload.editing){
|
|
|
|
this.handleTaskCanceled();
|
|
|
|
}
|
2016-10-21 14:42:46 +00:00
|
|
|
}
|
|
|
|
|
2016-11-14 21:32:05 +00:00
|
|
|
handleEditProject(){
|
|
|
|
this.editProjectDialog.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateProject(project){
|
2016-11-15 16:51:19 +00:00
|
|
|
return $.ajax({
|
|
|
|
url: `/api/projects/${this.state.data.id}/`,
|
|
|
|
contentType: 'application/json',
|
|
|
|
data: JSON.stringify({
|
|
|
|
name: project.name,
|
|
|
|
description: project.descr,
|
|
|
|
}),
|
|
|
|
dataType: 'json',
|
|
|
|
type: 'PATCH'
|
|
|
|
}).done(() => {
|
|
|
|
this.refresh();
|
|
|
|
});
|
2016-11-14 21:32:05 +00:00
|
|
|
}
|
|
|
|
|
2016-11-09 21:13:43 +00:00
|
|
|
viewMap(){
|
2016-11-15 16:51:19 +00:00
|
|
|
location.href = `/map/project/${this.state.data.id}/`;
|
2016-11-09 21:13:43 +00:00
|
|
|
}
|
|
|
|
|
2019-02-20 21:42:20 +00:00
|
|
|
handleImportTask = () => {
|
|
|
|
this.setState({importing: true});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleCancelImportTask = () => {
|
|
|
|
this.setState({importing: false});
|
|
|
|
}
|
|
|
|
|
2016-10-21 14:42:46 +00:00
|
|
|
render() {
|
2016-11-15 16:51:19 +00:00
|
|
|
const { refreshing, data } = this.state;
|
|
|
|
const numTasks = data.tasks.length;
|
|
|
|
|
2016-10-11 17:42:17 +00:00
|
|
|
return (
|
2016-11-15 16:51:19 +00:00
|
|
|
<li className={"project-list-item list-group-item " + (refreshing ? "refreshing" : "")}
|
2016-11-11 18:22:29 +00:00
|
|
|
href="javascript:void(0);"
|
2017-02-08 22:12:05 +00:00
|
|
|
ref={this.setRef("dropzone")}
|
|
|
|
>
|
2016-11-14 21:32:05 +00:00
|
|
|
|
|
|
|
<EditProjectDialog
|
|
|
|
ref={(domNode) => { this.editProjectDialog = domNode; }}
|
|
|
|
title="Edit Project"
|
|
|
|
saveLabel="Save Changes"
|
|
|
|
savingLabel="Saving changes..."
|
|
|
|
saveIcon="fa fa-edit"
|
2016-11-15 16:51:19 +00:00
|
|
|
projectName={data.name}
|
|
|
|
projectDescr={data.description}
|
2016-11-14 21:32:05 +00:00
|
|
|
saveAction={this.updateProject}
|
2017-06-15 19:47:00 +00:00
|
|
|
deleteAction={this.hasPermission("delete") ? this.handleDelete : undefined}
|
2016-11-14 21:32:05 +00:00
|
|
|
/>
|
|
|
|
|
2016-10-18 15:25:14 +00:00
|
|
|
<div className="row no-margin">
|
2016-11-14 15:58:00 +00:00
|
|
|
<ErrorMessage bind={[this, 'error']} />
|
2016-10-18 15:25:14 +00:00
|
|
|
<div className="btn-group pull-right">
|
2017-06-15 19:47:00 +00:00
|
|
|
{this.hasPermission("add") ?
|
2019-02-20 21:42:20 +00:00
|
|
|
<div className={"asset-download-buttons btn-group " + (this.state.upload.uploading ? "hide" : "")}>
|
|
|
|
<button type="button"
|
|
|
|
className="btn btn-primary btn-sm"
|
2018-01-18 18:03:02 +00:00
|
|
|
onClick={this.handleUpload}
|
2017-06-15 19:47:00 +00:00
|
|
|
ref={this.setRef("uploadButton")}>
|
2019-05-15 19:50:36 +00:00
|
|
|
<i className="glyphicon glyphicon-upload"></i>
|
|
|
|
Select Images and GCP
|
|
|
|
</button>
|
|
|
|
<button type="button"
|
|
|
|
className="btn btn-default btn-sm"
|
|
|
|
onClick={this.handleImportTask}>
|
2019-05-15 20:18:38 +00:00
|
|
|
<i className="glyphicon glyphicon-import"></i> Import
|
2019-05-15 19:50:36 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
2017-06-15 19:47:00 +00:00
|
|
|
: ""}
|
2018-01-18 18:03:02 +00:00
|
|
|
|
2016-10-18 15:25:14 +00:00
|
|
|
<button disabled={this.state.upload.error !== ""}
|
2018-01-18 18:03:02 +00:00
|
|
|
type="button"
|
|
|
|
className={"btn btn-danger btn-sm " + (!this.state.upload.uploading ? "hide" : "")}
|
2016-10-18 15:25:14 +00:00
|
|
|
onClick={this.cancelUpload}>
|
|
|
|
<i className="glyphicon glyphicon-remove-circle"></i>
|
|
|
|
Cancel Upload
|
|
|
|
</button>
|
|
|
|
|
2016-11-09 21:13:43 +00:00
|
|
|
<button type="button" className="btn btn-default btn-sm" onClick={this.viewMap}>
|
|
|
|
<i className="fa fa-globe"></i> View Map
|
2016-10-18 15:25:14 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
2016-10-11 20:37:00 +00:00
|
|
|
|
2016-10-25 14:47:49 +00:00
|
|
|
<span className="project-name">
|
2016-11-15 16:51:19 +00:00
|
|
|
{data.name}
|
2016-10-25 14:47:49 +00:00
|
|
|
</span>
|
|
|
|
<div className="project-description">
|
2016-11-15 16:51:19 +00:00
|
|
|
{data.description}
|
2016-10-25 14:47:49 +00:00
|
|
|
</div>
|
|
|
|
<div className="row project-links">
|
2016-11-15 16:51:19 +00:00
|
|
|
{numTasks > 0 ?
|
2016-11-14 21:32:05 +00:00
|
|
|
<span>
|
|
|
|
<i className='fa fa-tasks'>
|
|
|
|
</i> <a href="javascript:void(0);" onClick={this.toggleTaskList}>
|
2016-11-15 16:51:19 +00:00
|
|
|
{numTasks} Tasks <i className={'fa fa-caret-' + (this.state.showTaskList ? 'down' : 'right')}></i>
|
2016-11-14 21:32:05 +00:00
|
|
|
</a>
|
|
|
|
</span>
|
|
|
|
: ""}
|
|
|
|
|
|
|
|
<i className='fa fa-edit'>
|
|
|
|
</i> <a href="javascript:void(0);" onClick={this.handleEditProject}> Edit
|
2016-10-25 14:47:49 +00:00
|
|
|
</a>
|
|
|
|
</div>
|
2016-10-18 15:25:14 +00:00
|
|
|
</div>
|
2016-11-11 18:22:29 +00:00
|
|
|
<i className="drag-drop-icon fa fa-inbox"></i>
|
2016-10-18 15:25:14 +00:00
|
|
|
<div className="row">
|
2017-11-22 19:53:29 +00:00
|
|
|
{this.state.upload.uploading ? <UploadProgressBar {...this.state.upload}/> : ""}
|
|
|
|
{this.state.upload.resizing ?
|
|
|
|
<ProgressBar
|
|
|
|
current={this.state.upload.resizedImages}
|
|
|
|
total={this.state.upload.totalCount}
|
|
|
|
template={(info) => `Resized ${info.current} of ${info.total} images. Your browser might slow down during this process.`}
|
|
|
|
/>
|
|
|
|
: ""}
|
|
|
|
|
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
|
|
|
|
2017-07-18 20:07:53 +00:00
|
|
|
{this.state.upload.editing ?
|
2017-11-22 19:53:29 +00:00
|
|
|
<NewTaskPanel
|
2016-10-22 15:23:37 +00:00
|
|
|
onSave={this.handleTaskSaved}
|
2018-01-18 18:03:02 +00:00
|
|
|
onCancel={this.handleTaskCanceled}
|
2017-11-22 19:53:29 +00:00
|
|
|
filesCount={this.state.upload.totalCount}
|
|
|
|
showResize={true}
|
2018-12-31 21:50:51 +00:00
|
|
|
getFiles={() => this.state.upload.files }
|
2016-10-21 14:42:46 +00:00
|
|
|
/>
|
2016-10-22 15:23:37 +00:00
|
|
|
: ""}
|
|
|
|
|
2019-02-20 21:42:20 +00:00
|
|
|
{this.state.importing ?
|
|
|
|
<ImportTaskPanel
|
2019-02-21 19:16:48 +00:00
|
|
|
onImported={this.newTaskAdded}
|
2019-02-20 21:42:20 +00:00
|
|
|
onCancel={this.handleCancelImportTask}
|
|
|
|
projectId={this.state.data.id}
|
|
|
|
/>
|
|
|
|
: ""}
|
|
|
|
|
2016-11-14 21:32:05 +00:00
|
|
|
{this.state.showTaskList ?
|
|
|
|
<TaskList
|
|
|
|
ref={this.setRef("taskList")}
|
2016-11-15 16:51:19 +00:00
|
|
|
source={`/api/projects/${data.id}/tasks/?ordering=-created_at`}
|
2016-11-14 21:32:05 +00:00
|
|
|
onDelete={this.taskDeleted}
|
2017-04-13 21:03:42 +00:00
|
|
|
history={this.props.history}
|
2016-11-14 21:32:05 +00:00
|
|
|
/> : ""}
|
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;
|