diff --git a/src/client.js b/src/client.js
index 626a5c3..c12018b 100644
--- a/src/client.js
+++ b/src/client.js
@@ -64,7 +64,35 @@ const client = mozaik => {
};
})
;
- }
+ },
+ projectsMergeRequests({ projects }) {
+ const reqs = projects.map((project) => {
+ return buildApiRequest(`/projects/${encodeURIComponent(project)}/merge_requests`);
+ });
+ return Promise.props({
+ mergeRequests: Promise.all(reqs).then((data) => {
+ return data.map((item) => item.body);
+ })
+ });
+ },
+ groupMergeRequests({ groups, query={} }) {
+ const reqs = groups.map((group) => {
+ return buildApiRequest(`/groups/${encodeURIComponent(group)}/merge_requests`, query);
+ });
+ return Promise.props({
+ mergeRequests: Promise.all(reqs).then((data) => {
+ return data.map((item) => item.body);
+ })
+ });
+ },
+ projectPipelines({ project, query={} }) {
+ return Promise.props({
+ project: operations.project({ project }),
+ pipelines: buildApiRequest(`/projects/${encodeURIComponent(project)}/pipelines`, query).then((res) => {
+ return res.body;
+ })
+ });
+ },
};
return operations;
diff --git a/src/components/MergeRequest.jsx b/src/components/MergeRequest.jsx
new file mode 100644
index 0000000..30e7bdb
--- /dev/null
+++ b/src/components/MergeRequest.jsx
@@ -0,0 +1,55 @@
+import React, { Component, PropTypes } from 'react';
+import moment from 'moment';
+
+
+class MergeRequestItem extends Component {
+ render() {
+ const { mergeRequest } = this.props;
+
+ const cssClasses = `list__item`;
+
+ return (
+
+
+ #{mergeRequest.title}
+
+
+
+
+ by
+ {mergeRequest.author.name}
+
+
+ upvotes
+ {mergeRequest.upvotes}
+
+
+
+
+
+
+ );
+ }
+}
+
+MergeRequestItem.displayName = 'MergeRequestItem';
+
+MergeRequestItem.propTypes = {
+ project: PropTypes.shape({
+ web_url: PropTypes.string.isRequired
+ }).isRequired,
+ build: PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ status: PropTypes.string.isRequired,
+ finished_at: PropTypes.string,
+ commit: PropTypes.shape({
+ message: PropTypes.string.isRequired
+ })
+ }).isRequired
+};
+
+
+export default MergeRequestItem;
\ No newline at end of file
diff --git a/src/components/MergeRequestGaugeGroups.jsx b/src/components/MergeRequestGaugeGroups.jsx
new file mode 100644
index 0000000..ea647a2
--- /dev/null
+++ b/src/components/MergeRequestGaugeGroups.jsx
@@ -0,0 +1,105 @@
+import React, { Component, PropTypes } from 'react';
+import reactMixin from 'react-mixin';
+import { ListenerMixin } from 'reflux';
+import _ from 'lodash';
+import Mozaik from 'mozaik/browser';
+const { Gauge } = Mozaik.Component;
+
+
+class MergeRequestsGaugeGroups extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = { mergeRequestCount: 0 };
+ }
+
+ getApiRequest() {
+ const { groups } = this.props;
+
+ return {
+ id: `gitlab.groupMergeRequests.${ groups }`,
+ params: {
+ groups,
+ query: {
+ state: 'opened'
+ }
+ }
+ };
+ }
+
+ onApiData({ mergeRequests }) {
+ const flattenArray = (arr) => [].concat.apply([], arr);
+ this.setState({ mergeRequestCount: flattenArray(mergeRequests).length });
+ }
+
+ render() {
+ const { thresholds } = this.props;
+ const { mergeRequestCount } = this.state;
+
+ const cappedValue = Math.min(mergeRequestCount, _.max(thresholds.map(threshold => threshold.threshold)));
+ let message = null;
+ const normThresholds = thresholds.map(threshold => {
+ if (message === null && cappedValue <= threshold.threshold) {
+ message = threshold.message;
+ }
+
+ return {
+ upperBound: threshold.threshold,
+ color: threshold.color
+ };
+ });
+
+ return (
+
+
+ Pending Merge Requests
+
+ {mergeRequestCount}
+
+
+
+
+
+ );
+ }
+}
+
+MergeRequestsGaugeGroups.displayName = 'MergeRequestsGaugeGroups';
+
+MergeRequestsGaugeGroups.propTypes = {
+ project: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number
+ ]).isRequired,
+ thresholds: PropTypes.arrayOf(PropTypes.shape({
+ threshold: PropTypes.number.isRequired,
+ color: PropTypes.string.isRequired,
+ message: PropTypes.string.isRequired
+ })).isRequired
+};
+
+MergeRequestsGaugeGroups.defaultProps = {
+ thresholds: [
+ { threshold: 3, color: '#85e985', message: 'good job!' },
+ { threshold: 5, color: '#ecc265', message: 'you should consider reviewing' },
+ { threshold: 10, color: '#f26a3f', message: 'merge requests overflow' }
+ ]
+};
+
+reactMixin(MergeRequestsGaugeGroups.prototype, ListenerMixin);
+reactMixin(MergeRequestsGaugeGroups.prototype, Mozaik.Mixin.ApiConsumer);
+
+
+export default MergeRequestsGaugeGroups;
\ No newline at end of file
diff --git a/src/components/MergeRequests.jsx b/src/components/MergeRequests.jsx
new file mode 100644
index 0000000..3903f3d
--- /dev/null
+++ b/src/components/MergeRequests.jsx
@@ -0,0 +1,64 @@
+import React, { Component, PropTypes } from 'react';
+import reactMixin from 'react-mixin';
+import { ListenerMixin } from 'reflux';
+import Mozaik from 'mozaik/browser';
+import MergeRequestItem from './MergeRequest';
+
+
+class MergeRequests extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ mergeRequests: [],
+ groups: [],
+ query: {}
+ };
+ }
+
+ getApiRequest() {
+ const { groups, query } = this.props;
+
+ return {
+ id: `gitlab.groupMergeRequests.${ groups }`,
+ params: { groups, query }
+ };
+ }
+
+ onApiData({ groups, mergeRequests }) {
+ const flattenArray = (arr) => [].concat.apply([], arr);
+ this.setState({ groups, mergeRequests: flattenArray(mergeRequests) });
+ }
+
+ render() {
+ const { groups, mergeRequests } = this.state;
+
+ return (
+
+
+ MergeRequests
+
+ {mergeRequests.length}
+
+
+
+
+ {mergeRequests.map(mr => ())}
+
+
+ );
+ }
+}
+
+MergeRequests.propTypes = {
+ groups: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number
+ ]).isRequired
+};
+
+reactMixin(MergeRequests.prototype, ListenerMixin);
+reactMixin(MergeRequests.prototype, Mozaik.Mixin.ApiConsumer);
+
+
+export default MergeRequests;
\ No newline at end of file
diff --git a/src/components/PipelinesHistory.jsx b/src/components/PipelinesHistory.jsx
new file mode 100644
index 0000000..52ed78d
--- /dev/null
+++ b/src/components/PipelinesHistory.jsx
@@ -0,0 +1,59 @@
+import React, { Component, PropTypes } from 'react';
+import reactMixin from 'react-mixin';
+import { ListenerMixin } from 'reflux';
+import Mozaik from 'mozaik/browser';
+import PipelineHistoryItem from './PipelinesHistoryItem.jsx';
+
+class PipelinesHistory extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ project: null,
+ pipelines: []
+ };
+ }
+
+ getApiRequest() {
+ const { project, query } = this.props;
+
+ return {
+ id: `gitlab.projectPipelines.${project}`,
+ params: { project, query }
+ };
+ }
+
+ onApiData(data) {
+ const { project, pipelines } = data;
+ this.setState({ project, pipelines });
+ }
+
+ render() {
+ const { project, pipelines } = this.state;
+
+ return (
+
+
+ pipelines history
+
+
+
+ {pipelines.map((pipeline) => (
+
+ ))}
+
+
+ );
+ }
+}
+
+PipelinesHistory.displayName = 'PipelinesHistory';
+
+PipelinesHistory.propTypes = {
+ project: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]).isRequired
+};
+
+reactMixin(PipelinesHistory.prototype, ListenerMixin);
+reactMixin(PipelinesHistory.prototype, Mozaik.Mixin.ApiConsumer);
+
+export default PipelinesHistory;
\ No newline at end of file
diff --git a/src/components/PipelinesHistoryItem.jsx b/src/components/PipelinesHistoryItem.jsx
new file mode 100644
index 0000000..a695748
--- /dev/null
+++ b/src/components/PipelinesHistoryItem.jsx
@@ -0,0 +1,36 @@
+import React, { Component, PropTypes } from 'react';
+class PipelineHistoryItem extends Component {
+ render() {
+ const { project, pipeline } = this.props;
+
+ const cssClasses = `list__item list__item--with-status list__item--with-status--${pipeline.status}`;
+
+ return (
+
+ );
+ }
+}
+
+PipelineHistoryItem.displayName = 'PipelineHistoryItem';
+
+PipelineHistoryItem.propTypes = {
+ project: PropTypes.shape({
+ web_url: PropTypes.string.isRequired
+ }).isRequired,
+ pipeline: PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ status: PropTypes.string.isRequired,
+ }).isRequired
+};
+
+export default PipelineHistoryItem;
\ No newline at end of file
diff --git a/src/components/index.js b/src/components/index.js
index 8998f67..4ad9153 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -5,7 +5,9 @@ import BuildHistory from './BuildHistory.jsx';
import BuildHistogram from './BuildHistogram.jsx';
import Branches from './Branches.jsx';
import MergeRequestsGauge from './MergeRequestsGauge.jsx';
-
+import PipelinesHistory from './PipelinesHistory.jsx';
+import MergeRequests from './MergeRequests.jsx';
+import MergeRequestsGaugeGroups from './MergeRequestGaugeGroups.jsx';
export default {
Project,
@@ -14,5 +16,8 @@ export default {
BuildHistory,
BuildHistogram,
Branches,
- MergeRequestsGauge
+ MergeRequestsGauge,
+ PipelinesHistory,
+ MergeRequests,
+ MergeRequestsGaugeGroups
};