labo-components
Version:
299 lines (262 loc) • 10.3 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import IDUtil from '../../../../util/IDUtil';
import trunc from '../../../../util/Trunc';
import ProjectUtil from '../../../../util/ProjectUtil';
import LocalStorageHandler from '../../../../util/LocalStorageHandler';
import { exportDataAsJSON } from '../../helpers/Export';
import SortTable from '../../SortTable';
/**
* Table that shows all the projects. It handles the loading and filtering of the projects data.
*/
class ProjectTable extends React.PureComponent {
constructor(props) {
super(props);
this.head = [
{ field: 'name', content: 'Name', sortable: true },
{ field: 'description', content: 'Description', sortable: true },
{ field: 'owner', content: 'Owner', sortable: true },
{ field: 'access', content: 'Access', sortable: true },
{ field: 'created', content: 'Created', sortable: true },
{ field: '', content: '', sortable: false }
];
this.bulkActions = [
{ title: 'Delete', onApply: this.deleteProjects },
{ title: 'Export', onApply: exportDataAsJSON }
];
this.defaultSort = {
field: 'name',
order: 'asc'
}
this.state = {
projects: [], //prettified projects that are visible in the ui
rawProjects : [], //all unfiltered projects of instance Project
loading: true,
filter: {
keywords: '',
currentUser: false
},
};
this.requestedBookmark = {};
this.requestDataTimeout = -1;
}
componentDidMount() {
this.loadData();
}
componentDidUpdate() {
if (this.lastFilter !== this.state.filter) {
this.lastFilter = this.state.filter;
// throttle data requests
clearTimeout(this.requestDataTimeout);
this.requestDataTimeout = setTimeout(this.loadData, 500);
}
}
//load the projects from the Workspace API
loadData = () => {
this.props.api.list(
this.props.user.id,
this.state.filter,
this.setProjects
);
};
setProjects = projects => {
// decorate the projects
let uiProjects = this.toUIData(projects || []);
// we filter the results now on client side
uiProjects = this.filterProjects(uiProjects || []);
this.setState({
projects: uiProjects, // will be displayed in the table
rawProjects : projects, // needed for the ProjectMigrationForm
loading: false
});
};
//filter projects (client side) TODO: server side filtering
filterProjects = projects => {
const userId = this.props.user.id;
let result = projects.filter(project => project.getAccess(userId));
const filter = this.state.filter;
// filter on keywords
if (filter.keywords) {
const keywords = filter.keywords.split(' ');
keywords.forEach(k => {
k = k.toLowerCase();
result = result.filter(
project =>
(project.name && project.name.toLowerCase().includes(k)) ||
(project.description && project.description.toLowerCase().includes(k))
);
});
}
// filter on current user
if (filter.currentUser) {
result = result.filter(project => project.owner.id === userId);
}
return result;
};
//convert the api data to client side data;
toUIData = projects => {
return projects.map(p => {
p.getBookmarkCount = function() {
return this.bookmarks.length;
};
p.getAccess = function() {
return 'Admin';
};
p.getCollaboratorCount = function() {
return this.collaborators.length;
};
p.canDelete = function() {
return true;
};
p.canExport = function() {
return true;
};
p.canOpen = function() {
return true;
};
p.bookmarks = [];
p.collaborators = [];
p.owner = {
id: this.props.user.id,
name: this.props.user.name
};
return p;
});
};
//triggered upon using the filter field
keywordsChange = e => {
this.setState({
filter: Object.assign({}, this.state.filter, {
keywords: e.target.value
})
});
};
deleteProjects = projects => {
ProjectUtil.deleteProjects(projects, this.props.user.id, this.loadData)
};
sortProjects = (projects, sort) => {
const getLowerSafe = (s)=>(s ? s.toLowerCase() : '');
const getFirst = (l)=>(Array.isArray(l) && l[0] ? l[0].toLowerCase() : '');
const sorted = projects;
switch (sort.field) {
case 'name': sorted.sort((a, b) => getLowerSafe(a.name) > getLowerSafe(b.name) ? 1 : -1); break;
case 'description': sorted.sort((a, b) => getLowerSafe(a.description) > getLowerSafe(b.description) ? -1 : 1); break;
case 'owner': sorted.sort((a, b) => getLowerSafe(a.owner.name) > getLowerSafe(b.owner.name) ? 1 : -1); break;
case 'access': sorted.sort((a, b) => a.getAccess(this.props.user.id) > b.getAccess(this.props.user.id) ? 1 : -1); break;
case 'created': sorted.sort((a, b) => a.created > b.created ? 1 : -1) ; break;
// case 'collaborators': sorted.sort((a, b) => getFirst(a.collaborators) > getFirst(b.collaborators) ? 1 : -1) ; break;
default: return sorted;
}
return sort.order === 'desc' ? sorted.reverse() : sorted;
};
setActiveProject = project => {
LocalStorageHandler.storeJSONInLocalStorage('stored-active-project', project);
};
//Transforms a project to a row needed for the sort table
//don't like this is passed as a function to the sort table... but let's see first
getProjectRow = project => {
const currentUserId = this.props.user.id;
return [
{
props: { className: 'primary' },
content: (
<Link onClick={() => this.setActiveProject(project)}
to={'/workspace/projects/' + project.id}>
{project.name}
</Link>
)
},
{
props: { className: 'description' },
content: (
<p><Link onClick={() => this.setActiveProject(project)}
to={'/workspace/projects/' + project.id + '/details'}>
{trunc(project.description, 140)}
</Link></p>
)
},
{
content: (
<span>
{project.owner.name}{' '}
{project.getCollaboratorCount() ? (
<span className="collaborators">
{project.getCollaboratorCount()} Collaborator{project.getCollaboratorCount() !== 1 ? 's' : ''}
</span>
) : ('')
}
</span>
)
},
{
props: { className: 'access smaller' },
content: project.getAccess(currentUserId)
},
{
props: { className: 'smaller' },
content: project.created.substring(0, 10)
},
{
props: { className: 'actions' },
content: project.canOpen(currentUserId) ? (
<div>
<Link onClick={() => this.setActiveProject(project)}
to={'/workspace/projects/' + project.id}
className="btn">
Open
</Link>
<div className="row-menu">
<span>⋮</span>
<ul>
<li onClick={() => ProjectUtil.deleteProjects([project], this.props.user.id, this.loadData)}>Delete</li>
<li onClick={() => exportDataAsJSON(project)}>Export</li>
{/*}
<li onClick={() => ProjectUtil.moveProject(this.props.user, project, this.state.rawProjects)}>Move</li>
<li onClick={() => alert('Copy project' + project.name)}>Copy</li>
*/}
</ul>
</div>
</div>
) : ('')
}];
};
render() {
return (
<div className={IDUtil.cssClassName('project-table')}>
<div className="tools">
<div className="left">
<h3>Filters</h3>
<div className="filter-container">
<input
className="search"
type="text"
placeholder="Search User Projects"
value={this.state.filter.keywords}
onChange={this.keywordsChange}/>
</div>
</div>
</div>
<div id="project-migration-modal"/>
<SortTable
items={this.state.projects}
head={this.head}
row={this.getProjectRow}
onSort={this.sortProjects}
loading={this.state.loading}
bulkActions={this.bulkActions}
defaultSort={this.defaultSort}
/>
</div>
);
}
}
ProjectTable.propTypes = {
// project api
api: PropTypes.func.isRequired,
// current user object used for defining access roles per project
user: PropTypes.shape({
id: PropTypes.string.isRequired
}).isRequired
};
export default ProjectTable;