show a list of milestones for a project

This commit is contained in:
Radek Stepan 2016-01-18 20:52:24 +01:00
parent fee4d13696
commit eec0259fea
5 changed files with 109 additions and 56 deletions

View File

@ -92,6 +92,8 @@ export default React.createClass({
// Show project milestones. // Show project milestones.
milestones(owner, name) { milestones(owner, name) {
document.title = `${owner}/${name}`;
process.nextTick(() => { actions.emit('projects.load', { owner, name }); });
return <MilestonesPage owner={owner} name={name} />; return <MilestonesPage owner={owner} name={name} />;
}, },

View File

@ -11,7 +11,7 @@ import Link from './Link.jsx';
export default React.createClass({ export default React.createClass({
displayName: 'Projects.jsx', displayName: 'Milestones.jsx',
mixins: [ Format ], mixins: [ Format ],
@ -20,7 +20,7 @@ export default React.createClass({
}, },
render() { render() {
let projects = this.props.projects; let { projects, project } = this.props;
// Show the projects with errors first. // Show the projects with errors first.
let errors = _(projects.list).filter('errors').map((project, i) => { let errors = _(projects.list).filter('errors').map((project, i) => {
@ -36,12 +36,16 @@ export default React.createClass({
); );
}).value(); }).value();
// Now for the list of projects. // Now for the list of milestones.
let list = _.map(projects.index, ([ pI, mI ]) => { let list = [];
_.each(projects.index, ([ pI, mI ]) => {
let { owner, name, milestones } = projects.list[pI]; let { owner, name, milestones } = projects.list[pI];
let milestone = milestones[mI]; let milestone = milestones[mI];
return ( // Filter down?
if (!(!project || (project.owner == owner && project.name == name))) return;
list.push(
<tr className={cls({ 'done': milestone.stats.isDone })} key={`${pI}-${mI}`}> <tr className={cls({ 'done': milestone.stats.isDone })} key={`${pI}-${mI}`}>
<td className="repo"> <td className="repo">
<Link route={{ 'to': 'milestones', 'params': { owner, name } }} className="project"> <Link route={{ 'to': 'milestones', 'params': { owner, name } }} className="project">
@ -74,21 +78,38 @@ export default React.createClass({
// Wait for something to show. // Wait for something to show.
if (!errors.length && !list.length) return false; if (!errors.length && !list.length) return false;
return ( if (project) {
<div id="projects"> return (
<div className="header"> <div id="projects">
<a className="sort" onClick={this._onSort}><Icon name="sort"/> Sorted by {projects.sortBy}</a> <div className="header">
<h2>Projects</h2> <a className="sort" onClick={this._onSort}><Icon name="sort"/> Sorted by {projects.sortBy}</a>
<h2>Milestones</h2>
</div>
<table>
<tbody>
{list}
</tbody>
</table>
<div className="footer" />
</div> </div>
<table> );
<tbody> } else {
{errors} return (
{list} <div id="projects">
</tbody> <div className="header">
</table> <a className="sort" onClick={this._onSort}><Icon name="sort"/> Sorted by {projects.sortBy}</a>
<div className="footer" /> <h2>Projects</h2>
</div> </div>
); <table>
<tbody>
{errors}
{list}
</tbody>
</table>
<div className="footer" />
</div>
);
}
} }
}); });

View File

@ -4,6 +4,8 @@ import Page from '../mixins/Page.js';
import Notify from '../components/Notify.jsx'; import Notify from '../components/Notify.jsx';
import Header from '../components/Header.jsx'; import Header from '../components/Header.jsx';
import Milestones from '../components/Milestones.jsx';
import Hero from '../components/Hero.jsx';
export default React.createClass({ export default React.createClass({
@ -12,12 +14,25 @@ export default React.createClass({
mixins: [ Page ], mixins: [ Page ],
render() { render() {
let content;
if (!this.state.app.loading) {
let projects = this.state.projects;
content = <Milestones projects={projects} project={this.props} />;
}
return ( return (
<div> <div>
<Notify /> <Notify />
<Header {...this.state} /> <Header {...this.state} />
<div id="page" /> <div id="page">
<div id="title">
<div className="wrap">
<h2 className="title">{this.props.owner}/{this.props.name}</h2>
</div>
</div>
<div id="content" className="wrap">{content}</div>
</div>
<div id="footer"> <div id="footer">
<div className="wrap"> <div className="wrap">

View File

@ -4,7 +4,7 @@ import Page from '../mixins/Page.js';
import Notify from '../components/Notify.jsx'; import Notify from '../components/Notify.jsx';
import Header from '../components/Header.jsx'; import Header from '../components/Header.jsx';
import Projects from '../components/Projects.jsx'; import Milestones from '../components/Milestones.jsx';
import Hero from '../components/Hero.jsx'; import Hero from '../components/Hero.jsx';
export default React.createClass({ export default React.createClass({
@ -18,8 +18,7 @@ export default React.createClass({
if (!this.state.app.loading) { if (!this.state.app.loading) {
let projects = this.state.projects; let projects = this.state.projects;
if (projects.list.length) { if (projects.list.length) {
// Show a list of projects. content = <Milestones projects={projects} />;
content = <Projects projects={projects} />;
} else { } else {
content = <Hero />; content = <Hero />;
} }

View File

@ -59,46 +59,62 @@ class ProjectsStore extends Store {
}); });
} }
// Start fetching milestones and issues for projects. // Fetch milestones and issues for a project(s).
onProjectsLoad() { onProjectsLoad(project) {
let projects = this.get('list'); let projects = this.get('list');
// Quit if we have no projects. // Quit if we have no projects.
if (!projects.length) return; if (!projects.length) return;
// Fetch milestones and issues for a project.
let get = (user, p) => {
// Fetch their milestones.
milestones.fetchAll(user, p, this.cb((err, milestones) => { // async
// Save the error if project does not exist.
if (err) return this.saveError(p, err);
// Now add in the issues.
milestones.forEach((milestone) => {
// Do we have this milestone already? Skip fetching issues then.
if (_.find(p.milestones, ({ number }) => {
return milestone.number === number;
})) {
return;
}
// OK fetch all the issues for this milestone then.
issues.fetchAll(user, {
'owner': p.owner,
'name': p.name,
'milestone': milestone.number
}, this.cb((err, obj) => { // async
// Save any errors on the project.
if (err) return this.saveError(p, err);
// Add in the issues to the milestone.
_.extend(milestone, { 'issues': obj });
// Save the milestone.
this.addMilestone(p, milestone);
}));
});
}));
};
// Wait for the user to get resolved. // Wait for the user to get resolved.
this.get('user', this.cb((user) => { // async this.get('user', this.cb((user) => { // async
// For all projects. if (project) {
projects.forEach((project) => { // For a single project.
// Fetch their milestones. _.find(this.get('list'), (obj) => {
milestones.fetchAll(user, project, this.cb((err, milestones) => { // async if (project.owner == obj.owner && project.name == obj.name) {
// Save the error if project does not exist. project = obj; // expand by saved properties
if (err) return this.saveError(project, err); return true;
// Now add in the issues. };
milestones.forEach((milestone) => { return false;
// Do we have this milestone already? Skip fetching issues then. });
if (_.find(project.milestones, ({ number }) => { // For a single project.
return milestone.number === number; get(user, project);
})) { } else {
return; // For all projects.
} projects.forEach(_.partial(get, user));
}
// OK fetch all the issues for this milestone then.
issues.fetchAll(user, {
'owner': project.owner,
'name': project.name,
'milestone': milestone.number
}, this.cb((err, obj) => { // async
// Save any errors on the project.
if (err) return this.saveError(project, err);
// Add in the issues to the milestone.
_.extend(milestone, { 'issues': obj });
// Save the milestone.
this.addMilestone(project, milestone);
}));
});
}));
});
})); }));
} }