32 | {branch.name}
33 |
34 | {commit &&
35 | commit.author && (
36 |
37 | by{' '}
38 |
39 | {commit.author.login}
40 |
41 |
42 | )}
43 |
44 | }
45 | post={
46 | commit &&
47 | commit.author && (
48 |
49 |
50 |
51 | )
52 | }
53 | />
54 | )
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/Branches.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import {
4 | TrapApiError,
5 | Widget,
6 | WidgetHeader,
7 | WidgetBody,
8 | WidgetLoader,
9 | GitBranchIcon,
10 | } from '@mozaik/ui'
11 | import Branch, { BranchPropType } from './Branch'
12 |
13 | export default class Branches extends Component {
14 | static propTypes = {
15 | repository: PropTypes.string.isRequired,
16 | title: PropTypes.string,
17 | apiData: PropTypes.shape({
18 | branches: PropTypes.arrayOf(BranchPropType).isRequired,
19 | }),
20 | apiError: PropTypes.object,
21 | }
22 |
23 | static getApiRequest({ repository }) {
24 | return {
25 | id: `github.branches.${repository}`,
26 | params: { repository },
27 | }
28 | }
29 |
30 | render() {
31 | const { repository, title, apiData, apiError } = this.props
32 |
33 | let body =
34 | let count
35 | if (apiData && !apiError) {
36 | count = apiData.branches.length
37 | body = (
38 |
39 | {apiData.branches.map(branch => (
40 |
41 | ))}
42 |
43 | )
44 | }
45 |
46 | return (
47 |
48 |
54 |
55 | {body}
56 |
57 |
58 | )
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/Status.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import {
5 | TrapApiError,
6 | Widget,
7 | WidgetHeader,
8 | WidgetBody,
9 | WidgetStatusBadge,
10 | ClockIcon,
11 | GithubIcon,
12 | } from '@mozaik/ui'
13 |
14 | export default class Status extends Component {
15 | static propTypes = {
16 | apiData: PropTypes.shape({
17 | status: PropTypes.string.isRequired,
18 | body: PropTypes.string.isRequired,
19 | }),
20 | apiError: PropTypes.object,
21 | }
22 |
23 | static getApiRequest() {
24 | return { id: 'github.status' }
25 | }
26 |
27 | render() {
28 | const { apiData: _status, apiError } = this.props
29 |
30 | let status = 'unknown'
31 | let messageNode
32 | let meta
33 | if (_status) {
34 | status = _status.status
35 | messageNode = _status.body
36 | meta = (
37 |
38 |
39 |
40 | {moment(_status.created_on).fromNow()}
41 |
42 | )
43 | }
44 |
45 | return (
46 |
47 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | )
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/badges/OrgBadge.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import {
4 | TrapApiError,
5 | Widget,
6 | WidgetLabel,
7 | WidgetHeader,
8 | WidgetBody,
9 | WidgetLoader,
10 | WidgetAvatar,
11 | ExternalLink,
12 | GithubIcon,
13 | } from '@mozaik/ui'
14 |
15 | export default class OrgBadge extends Component {
16 | static propTypes = {
17 | organization: PropTypes.string.isRequired,
18 | title: PropTypes.string,
19 | apiData: PropTypes.shape({}),
20 | apiError: PropTypes.object,
21 | }
22 |
23 | static getApiRequest({ organization }) {
24 | return {
25 | id: `github.organization.${organization}`,
26 | params: { organization },
27 | }
28 | }
29 |
30 | render() {
31 | const { organization, title, apiData: orgInfo, apiError } = this.props
32 |
33 | let body =
34 | if (orgInfo) {
35 | body = (
36 |
47 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
67 | {orgInfo.description}
68 |
69 |
76 |
79 | public repos
80 |
81 | }
82 | prefix={orgInfo.public_repos}
83 | style={{ width: '48%', marginBottom: '1vmin' }}
84 | />
85 |
90 |
95 |
100 |
101 |
102 | )
103 | }
104 |
105 | return (
106 |
107 |
112 |
113 | {body}
114 |
115 |
116 | )
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/components/badges/RepoBadge.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import {
4 | TrapApiError,
5 | Widget,
6 | WidgetHeader,
7 | WidgetBody,
8 | WidgetLoader,
9 | WidgetLabel as Label,
10 | ExternalLink,
11 | GithubIcon,
12 | } from '@mozaik/ui'
13 |
14 | export default class RepoBadge extends Component {
15 | static propTypes = {
16 | repository: PropTypes.string.isRequired,
17 | title: PropTypes.string,
18 | apiData: PropTypes.object,
19 | apiError: PropTypes.object,
20 | showKeys: PropTypes.arrayOf(PropTypes.string).isRequired,
21 | }
22 |
23 | static defaultProps = {
24 | showKeys: ['description'],
25 | }
26 |
27 | static getApiRequest({ repository }) {
28 | return {
29 | id: `github.repository.${repository}`,
30 | params: { repository },
31 | }
32 | }
33 |
34 | render() {
35 | const { repository, title, apiData: repoInfo, apiError } = this.props
36 |
37 | let body =
38 | if (repoInfo) {
39 | const labelStyle = { width: '48%', marginBottom: '1vmin' }
40 |
41 | body = (
42 |
51 |
57 | {repoInfo.description}
58 |
59 |
66 |
103 |
104 | )
105 | }
106 |
107 | return (
108 |
109 |
114 |
115 | {body}
116 |
117 |
118 | )
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/components/badges/UserBadge.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import {
4 | TrapApiError,
5 | Widget,
6 | WidgetHeader,
7 | WidgetBody,
8 | WidgetLoader,
9 | WidgetLabel,
10 | WidgetAvatar,
11 | ExternalLink,
12 | GithubIcon,
13 | } from '@mozaik/ui'
14 |
15 | export default class UserBadge extends Component {
16 | static propTypes = {
17 | user: PropTypes.string.isRequired,
18 | title: PropTypes.string,
19 | apiData: PropTypes.shape({}),
20 | apiError: PropTypes.object,
21 | }
22 |
23 | static getApiRequest({ user }) {
24 | return {
25 | id: `github.user.${user}`,
26 | params: { user },
27 | }
28 | }
29 |
30 | render() {
31 | const { title, apiData: user, apiError } = this.props
32 |
33 | let body =
34 | if (user) {
35 | body = (
36 |
46 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
70 | public repos
71 |
72 | }
73 | prefix={user.public_repos}
74 | style={{ width: '48%', marginBottom: '1vmin' }}
75 | />
76 |
81 |
84 | followers
85 |
86 | }
87 | prefix={user.followers}
88 | style={{ width: '48%', marginBottom: '1vmin' }}
89 | />
90 |
93 | following
94 |
95 | }
96 | prefix={user.following}
97 | style={{ width: '48%', marginBottom: '1vmin' }}
98 | />
99 |
104 |
105 |
106 | )
107 | }
108 |
109 | return (
110 |
111 |
116 |
117 | {body}
118 |
119 |
120 | )
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the Mozaïk project.
3 | *
4 | * (c) 2016 Raphaël Benitte
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | import Branches from './Branches'
11 | import PullRequests from './pull-requests/PullRequests'
12 | import UserBadge from './badges/UserBadge'
13 | import OrgBadge from './badges/OrgBadge'
14 | import RepoBadge from './badges/RepoBadge'
15 | import RepoContributorsStats from './stats/RepoContributorsStats'
16 | import Status from './Status'
17 | import RepoTrafficViewsHistogram from './traffic/RepoTrafficViewsHistogram'
18 | import RepoTrafficViewsLine from './traffic/RepoTrafficViewsLine'
19 | import RepoTrafficClonesHistogram from './traffic/RepoTrafficClonesHistogram'
20 | import RepoTrafficClonesLine from './traffic/RepoTrafficClonesLine'
21 | import RepoCommitActivityHistogram from './stats/RepoCommitActivityHistogram'
22 | import RepoCommitActivityLine from './stats/RepoCommitActivityLine'
23 |
24 | export default {
25 | Branches,
26 | PullRequests,
27 | UserBadge,
28 | OrgBadge,
29 | RepoBadge,
30 | RepoContributorsStats,
31 | Status,
32 | RepoTrafficViewsHistogram,
33 | RepoTrafficViewsLine,
34 | RepoTrafficClonesHistogram,
35 | RepoTrafficClonesLine,
36 | RepoCommitActivityHistogram,
37 | RepoCommitActivityLine,
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/pull-requests/PullRequest.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import { WidgetListItem, WidgetAvatar, ExternalLink, ClockIcon } from '@mozaik/ui'
5 |
6 | export default class PullRequest extends Component {
7 | static propTypes = {
8 | pullRequest: PropTypes.shape({
9 | title: PropTypes.string.isRequired,
10 | html_url: PropTypes.string.isRequired,
11 | created_at: PropTypes.string.isRequired,
12 | user: PropTypes.shape({
13 | html_url: PropTypes.string.isRequired,
14 | avatar_url: PropTypes.string.isRequired,
15 | login: PropTypes.string.isRequired,
16 | }).isRequired,
17 | }).isRequired,
18 | }
19 |
20 | render() {
21 | const { pullRequest } = this.props
22 | const { title, html_url, created_at, user } = pullRequest
23 |
24 | return (
25 |
28 | {title} by{' '}
29 | {user.login}
30 |
31 | }
32 | pre={
33 |
34 |
35 |
36 | }
37 | meta={
38 |
44 |
45 |
46 | {moment(created_at).fromNow()}
47 |
48 | }
49 | align="top"
50 | />
51 | )
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/pull-requests/PullRequests.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import {
4 | TrapApiError,
5 | Widget,
6 | WidgetHeader,
7 | WidgetBody,
8 | WidgetLoader,
9 | GithubIcon,
10 | } from '@mozaik/ui'
11 | import PullRequest from './PullRequest'
12 |
13 | export default class PullRequests extends Component {
14 | static propTypes = {
15 | repository: PropTypes.string.isRequired,
16 | title: PropTypes.string,
17 | apiData: PropTypes.shape({
18 | pullRequests: PropTypes.arrayOf(PropTypes.object).isRequired,
19 | }),
20 | apiError: PropTypes.object,
21 | }
22 |
23 | static getApiRequest({ repository }) {
24 | return {
25 | id: `github.pullRequests.${repository}`,
26 | params: { repository },
27 | }
28 | }
29 |
30 | render() {
31 | const { repository, title, apiData, apiError } = this.props
32 |
33 | let body =
34 | let count = 0
35 | if (apiData) {
36 | count = apiData.pullRequests.length
37 | body = (
38 |
39 | {apiData.pullRequests.map(pullRequest => (
40 |
41 | ))}
42 |
43 | )
44 | }
45 |
46 | return (
47 |
48 |
54 |
55 | {body}
56 |
57 |
58 | )
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/stats/RepoCommitActivity.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import RepoCommitActivityHistogramChart from './charts/RepoCommitActivityHistogramChart'
4 | import RepoCommitActivityLineChart from './charts/RepoCommitActivityLineChart'
5 | import {
6 | TrapApiError,
7 | Widget,
8 | WidgetHeader,
9 | WidgetBody,
10 | WidgetLoader,
11 | GithubIcon,
12 | } from '@mozaik/ui'
13 |
14 | export default class RepositoryCommitActivity extends Component {
15 | static propTypes = {
16 | repository: PropTypes.string.isRequired,
17 | title: PropTypes.string,
18 | apiData: PropTypes.shape({
19 | buckets: PropTypes.arrayOf(PropTypes.object).isRequired,
20 | }),
21 | apiError: PropTypes.object,
22 | type: PropTypes.oneOf(['histogram', 'line']).isRequired,
23 | theme: PropTypes.object.isRequired,
24 | }
25 |
26 | static getApiRequest({ repository }) {
27 | return {
28 | id: `github.repoCommitActivity.${repository}`,
29 | params: { repository },
30 | }
31 | }
32 |
33 | render() {
34 | const { repository, title, type, apiData, apiError, theme } = this.props
35 |
36 | let body =
37 | if (apiData && !apiError) {
38 | if (type === 'histogram') {
39 | body =
40 | } else if (type === 'line') {
41 | const chartData = [
42 | {
43 | id: 'commits',
44 | data: apiData.buckets.map(datum =>
45 | Object.assign({}, datum, {
46 | x: datum.week,
47 | y: datum.total,
48 | })
49 | ),
50 | },
51 | ]
52 |
53 | body =
54 | }
55 | }
56 |
57 | return (
58 |
59 |
64 |
65 | {body}
66 |
67 |
68 | )
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/stats/RepoCommitActivityHistogram.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import RepoCommitActivity from './RepoCommitActivity'
3 |
4 | export default class RepoCommitActivityHistogram extends Component {
5 | static getApiRequest(params) {
6 | return RepoCommitActivity.getApiRequest(params)
7 | }
8 |
9 | render() {
10 | return
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/stats/RepoCommitActivityLine.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import RepoCommitActivity from './RepoCommitActivity'
3 |
4 | export default class RepoCommitActivityLine extends Component {
5 | static getApiRequest(params) {
6 | return RepoCommitActivity.getApiRequest(params)
7 | }
8 |
9 | render() {
10 | return
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/stats/RepoContributorStat.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { WidgetListItem, WidgetAvatar, ExternalLink, GitCommitIcon } from '@mozaik/ui'
4 |
5 | export default class RepoContributorStat extends Component {
6 | static propTypes = {
7 | contributor: PropTypes.shape({
8 | total: PropTypes.number.isRequired,
9 | author: PropTypes.shape({
10 | login: PropTypes.string.isRequired,
11 | avatar_url: PropTypes.string.isRequired,
12 | }).isRequired,
13 | }).isRequired,
14 | }
15 |
16 | render() {
17 | const {
18 | contributor: { author, total },
19 | } = this.props
20 |
21 | return (
22 |
25 | {author.login}
26 |
27 | }
28 | pre={
29 |
30 |
31 |
32 | }
33 | post={
34 |
40 | {total}
41 |
42 |
43 |
44 | }
45 | />
46 | )
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/stats/RepoContributorsStats.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import {
4 | TrapApiError,
5 | Widget,
6 | WidgetHeader,
7 | WidgetBody,
8 | WidgetLoader,
9 | GithubIcon,
10 | } from '@mozaik/ui'
11 | import RepoContributorStat from './RepoContributorStat'
12 |
13 | export default class RepoContributorsStats extends Component {
14 | static propTypes = {
15 | repository: PropTypes.string.isRequired,
16 | title: PropTypes.string,
17 | apiData: PropTypes.shape({
18 | contributors: PropTypes.arrayOf(PropTypes.object).isRequired,
19 | }),
20 | apiError: PropTypes.object,
21 | }
22 |
23 | static getApiRequest({ repository }) {
24 | return {
25 | id: `github.repositoryContributorsStats.${repository}`,
26 | params: { repository },
27 | }
28 | }
29 |
30 | render() {
31 | const { repository, title, apiData, apiError } = this.props
32 |
33 | let body =
34 | let count
35 | if (apiData && !apiError) {
36 | const contributors = apiData.contributors
37 | .slice()
38 | .sort((contribA, contribB) => contribB.total - contribA.total)
39 |
40 | count = contributors.length
41 | body = (
42 |
43 | {contributors.map(contributor => (
44 |
48 | ))}
49 |
50 | )
51 | }
52 |
53 | return (
54 |
55 |
61 |
62 | {body}
63 |
64 |
65 | )
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/stats/charts/RepoCommitActivityHistogramChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import { ResponsiveBar } from 'nivo'
5 |
6 | const margin = { top: 10, right: 10, bottom: 54, left: 60 }
7 | const format = d => moment.unix(d).format('MM/DD')
8 | const axisLeft = {
9 | legend: 'commits',
10 | legendPosition: 'center',
11 | legendOffset: -40,
12 | }
13 | const axisBottom = {
14 | format,
15 | tickRotation: -60,
16 | }
17 |
18 | export default class RepoCommitActivityHistogramChart extends Component {
19 | static propTypes = {
20 | commits: PropTypes.array.isRequired,
21 | theme: PropTypes.object.isRequired,
22 | }
23 |
24 | render() {
25 | const { commits, theme } = this.props
26 |
27 | return (
28 |
43 | )
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/stats/charts/RepoCommitActivityLineChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import { ResponsiveLine } from 'nivo'
5 |
6 | const margin = { top: 10, right: 20, bottom: 54, left: 60 }
7 | const format = d => moment.unix(d).format('MM/DD')
8 | const axisLeft = {
9 | legend: 'commits',
10 | legendPosition: 'center',
11 | legendOffset: -40,
12 | }
13 | const axisBottom = {
14 | format,
15 | tickRotation: -60,
16 | }
17 |
18 | export default class RepoCommitActivityLineChart extends Component {
19 | static propTypes = {
20 | commits: PropTypes.array.isRequired,
21 | theme: PropTypes.object.isRequired,
22 | }
23 |
24 | render() {
25 | const { commits, theme } = this.props
26 |
27 | return (
28 |
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/traffic/RepoTrafficClones.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import RepoTrafficClonesHistogramChart from './charts/RepoTrafficClonesHistogramChart'
4 | import RepoTrafficClonesLineChart from './charts/RepoTrafficClonesLineChart'
5 | import {
6 | TrapApiError,
7 | Widget,
8 | WidgetHeader,
9 | WidgetBody,
10 | WidgetLoader,
11 | GithubIcon,
12 | } from '@mozaik/ui'
13 |
14 | export default class RepoTrafficClones extends Component {
15 | static propTypes = {
16 | repository: PropTypes.string.isRequired,
17 | title: PropTypes.string,
18 | apiData: PropTypes.any,
19 | apiError: PropTypes.object,
20 | type: PropTypes.oneOf(['histogram', 'line']).isRequired,
21 | theme: PropTypes.object.isRequired,
22 | }
23 |
24 | static defaultProps = {
25 | type: 'histogram',
26 | }
27 |
28 | static getApiRequest({ repository }) {
29 | return {
30 | id: `github.trafficClones.${repository}`,
31 | params: { repository },
32 | }
33 | }
34 |
35 | render() {
36 | const { repository, title, type, apiData, apiError, theme } = this.props
37 |
38 | let countNode = null
39 | let body =
40 | if (apiData !== undefined) {
41 | const { count, uniques, clones } = apiData
42 |
43 | countNode = (
44 |
45 | {count} clones - {uniques} unique clones
46 |
47 | )
48 |
49 | if (type === 'histogram') {
50 | const chartData = clones.map(({ timestamp, uniques, count }) => ({
51 | timestamp,
52 | uniques,
53 | others: count - uniques,
54 | }))
55 |
56 | body =
57 | } else if (type === 'line') {
58 | const chartData = [
59 | {
60 | id: 'total',
61 | data: clones.map(clone => ({
62 | y: clone.count,
63 | x: clone.timestamp,
64 | })),
65 | },
66 | {
67 | id: 'uniques',
68 | data: clones.map(clone => ({
69 | y: clone.uniques,
70 | x: clone.timestamp,
71 | })),
72 | },
73 | ]
74 |
75 | body =
76 | }
77 | }
78 |
79 | return (
80 |
81 |
87 |
88 | {body}
89 |
90 |
91 | )
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/components/traffic/RepoTrafficClonesHistogram.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import RepoTrafficClones from './RepoTrafficClones'
3 |
4 | export default class RepoTrafficClonesHistogram extends Component {
5 | static getApiRequest(params) {
6 | return RepoTrafficClones.getApiRequest(params)
7 | }
8 |
9 | render() {
10 | return
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/traffic/RepoTrafficClonesLine.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import RepoTrafficClones from './RepoTrafficClones'
3 |
4 | export default class RepoTrafficLineHistogram extends Component {
5 | static getApiRequest(params) {
6 | return RepoTrafficClones.getApiRequest(params)
7 | }
8 |
9 | render() {
10 | return
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/traffic/RepoTrafficViews.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import RepoTrafficViewsHistogramChart from './charts/RepoTrafficViewsHistogramChart'
4 | import RepoTrafficViewsLineChart from './charts/RepoTrafficViewsLineChart'
5 | import { TrapApiError, Widget, WidgetHeader, WidgetBody, GithubIcon } from '@mozaik/ui'
6 |
7 | export default class RepoTrafficViews extends Component {
8 | static propTypes = {
9 | repository: PropTypes.string.isRequired,
10 | title: PropTypes.string,
11 | apiData: PropTypes.any,
12 | apiError: PropTypes.object,
13 | type: PropTypes.oneOf(['histogram', 'line']).isRequired,
14 | theme: PropTypes.object.isRequired,
15 | }
16 |
17 | static getApiRequest({ repository }) {
18 | return {
19 | id: `github.trafficViews.${repository}`,
20 | params: { repository },
21 | }
22 | }
23 |
24 | render() {
25 | const { repository, type, apiData, apiError, theme } = this.props
26 |
27 | let countNode = null
28 | let body = null
29 | if (apiData !== undefined) {
30 | const { count, uniques, views } = apiData
31 |
32 | countNode = (
33 |
34 | {count} views - {uniques} unique visitors
35 |
36 | )
37 |
38 | if (type === 'histogram') {
39 | const chartData = views.map(({ timestamp, uniques, count }) => ({
40 | timestamp,
41 | uniques,
42 | others: count - uniques,
43 | }))
44 |
45 | body =
46 | } else if (type === 'line') {
47 | const chartData = [
48 | {
49 | id: 'total',
50 | data: views.map(view => ({
51 | y: view.count,
52 | x: view.timestamp,
53 | })),
54 | },
55 | {
56 | id: 'uniques',
57 | data: views.map(view => ({
58 | y: view.uniques,
59 | x: view.timestamp,
60 | })),
61 | },
62 | ]
63 |
64 | body =
65 | }
66 | }
67 |
68 | return (
69 |
70 |
76 |
77 | {body}
78 |
79 |
80 | )
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/components/traffic/RepoTrafficViewsHistogram.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import RepoTrafficViews from './RepoTrafficViews'
3 |
4 | export default class RepoTrafficViewsHistogram extends Component {
5 | static getApiRequest(params) {
6 | return RepoTrafficViews.getApiRequest(params)
7 | }
8 |
9 | render() {
10 | return
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/traffic/RepoTrafficViewsLine.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import RepoTrafficViews from './RepoTrafficViews'
3 |
4 | export default class RepoTrafficViewsLine extends Component {
5 | static getApiRequest(params) {
6 | return RepoTrafficViews.getApiRequest(params)
7 | }
8 |
9 | render() {
10 | return
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/traffic/charts/RepoTrafficClonesHistogramChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import { ResponsiveBar } from 'nivo'
5 |
6 | const margin = { top: 10, right: 10, bottom: 54, left: 60 }
7 | const format = d => moment(d).format('MM/DD')
8 | const axisLeft = {
9 | legend: 'clones',
10 | legendPosition: 'center',
11 | legendOffset: -40,
12 | }
13 | const axisBottom = {
14 | format,
15 | tickRotation: -60,
16 | }
17 |
18 | export default class RepoTrafficClonesHistogramChart extends Component {
19 | static propTypes = {
20 | clones: PropTypes.array.isRequired,
21 | theme: PropTypes.object.isRequired,
22 | }
23 |
24 | render() {
25 | const { clones, theme } = this.props
26 |
27 | return (
28 |
42 | )
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/traffic/charts/RepoTrafficClonesLineChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import { ResponsiveLine } from 'nivo'
5 |
6 | const margin = { top: 10, right: 20, bottom: 54, left: 60 }
7 | const format = d => moment(d).format('MM/DD')
8 | const axisLeft = {
9 | legend: 'clones',
10 | legendPosition: 'center',
11 | legendOffset: -40,
12 | }
13 | const axisBottom = {
14 | format,
15 | tickRotation: -60,
16 | }
17 |
18 | export default class RepoTrafficClonesLineChart extends Component {
19 | static propTypes = {
20 | clones: PropTypes.array.isRequired,
21 | theme: PropTypes.object.isRequired,
22 | }
23 |
24 | render() {
25 | const { clones, theme } = this.props
26 |
27 | return (
28 |
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/traffic/charts/RepoTrafficViewsHistogramChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import { ResponsiveBar } from 'nivo'
5 |
6 | const margin = { top: 10, right: 10, bottom: 54, left: 60 }
7 | const format = d => moment(d).format('MM/DD')
8 | const axisLeft = {
9 | legend: 'visitors',
10 | legendPosition: 'center',
11 | legendOffset: -40,
12 | }
13 | const axisBottom = {
14 | format,
15 | tickRotation: -60,
16 | }
17 |
18 | export default class RepoTrafficViewsHistogramChart extends Component {
19 | static propTypes = {
20 | views: PropTypes.array.isRequired,
21 | theme: PropTypes.object.isRequired,
22 | }
23 |
24 | render() {
25 | const { views, theme } = this.props
26 |
27 | return (
28 |
43 | )
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/traffic/charts/RepoTrafficViewsLineChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import moment from 'moment'
4 | import { ResponsiveLine } from 'nivo'
5 |
6 | const margin = { top: 10, right: 20, bottom: 54, left: 60 }
7 | const format = d => moment(d).format('MM/DD')
8 | const axisLeft = {
9 | legend: 'visitors',
10 | legendPosition: 'center',
11 | legendOffset: -40,
12 | }
13 | const axisBottom = {
14 | format,
15 | tickRotation: -60,
16 | }
17 |
18 | export default class RepoTrafficViewsLineChart extends Component {
19 | static propTypes = {
20 | views: PropTypes.array.isRequired,
21 | theme: PropTypes.object.isRequired,
22 | }
23 |
24 | render() {
25 | const { views, theme } = this.props
26 |
27 | return (
28 |
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the Mozaïk project.
3 | *
4 | * (c) 2016 Raphaël Benitte
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | const convict = require('convict')
11 |
12 | const config = convict({
13 | github: {
14 | baseUrl: {
15 | doc: 'The github API base url.',
16 | default: 'https://api.github.com',
17 | format: String,
18 | env: 'GITHUB_BASE_URL',
19 | },
20 | token: {
21 | doc: 'The github API token.',
22 | default: '',
23 | format: String,
24 | env: 'GITHUB_API_TOKEN',
25 | },
26 | },
27 | })
28 |
29 | module.exports = config
30 |
--------------------------------------------------------------------------------
/test/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | globals:
2 | test: true
3 | expect: true
4 | describe: true
5 | it: true
6 |
--------------------------------------------------------------------------------
/test/components/badges/OrgBadge.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import renderer from 'react-test-renderer'
4 | import { ThemeProvider } from 'styled-components'
5 | import { WidgetHeader, WidgetLoader, defaultTheme } from '@mozaik/ui'
6 | import OrgBadge from './../../../src/components/badges/OrgBadge'
7 |
8 | const sampleOrganization = 'github'
9 |
10 | test('should return correct api request', () => {
11 | expect(
12 | OrgBadge.getApiRequest({
13 | organization: sampleOrganization,
14 | })
15 | ).toEqual({
16 | id: `github.organization.${sampleOrganization}`,
17 | params: { organization: sampleOrganization },
18 | })
19 | })
20 |
21 | test('should display loader if no apiData available', () => {
22 | const wrapper = shallow()
23 |
24 | expect(wrapper.find(WidgetLoader).exists()).toBeTruthy()
25 | })
26 |
27 | test('should be able to display organization name without api response', () => {
28 | const wrapper = shallow()
29 |
30 | const header = wrapper.find(WidgetHeader)
31 | expect(header.exists()).toBeTruthy()
32 | expect(header.prop('subject')).toBe(sampleOrganization)
33 | })
34 |
35 | test('should allow title override', () => {
36 | const wrapper = shallow()
37 |
38 | const header = wrapper.find(WidgetHeader)
39 | expect(header.exists()).toBeTruthy()
40 | expect(header.prop('title')).toBe('override')
41 | expect(header.prop('subject')).toBe(null)
42 | })
43 |
44 | test('should render as expected', () => {
45 | const tree = renderer.create(
46 |
47 |
48 |
49 | )
50 |
51 | expect(tree).toMatchSnapshot()
52 | })
53 |
--------------------------------------------------------------------------------
/test/components/badges/UserBadge.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import renderer from 'react-test-renderer'
4 | import { ThemeProvider } from 'styled-components'
5 | import { WidgetHeader, WidgetLoader, defaultTheme } from '@mozaik/ui'
6 | import UserBadge from './../../../src/components/badges/UserBadge'
7 |
8 | const sampleUser = 'plouc'
9 |
10 | test('should return correct api request', () => {
11 | expect(
12 | UserBadge.getApiRequest({
13 | user: sampleUser,
14 | })
15 | ).toEqual({
16 | id: `github.user.${sampleUser}`,
17 | params: { user: sampleUser },
18 | })
19 | })
20 |
21 | test('should display loader if no apiData available', () => {
22 | const wrapper = shallow()
23 |
24 | expect(wrapper.find(WidgetLoader).exists()).toBeTruthy()
25 | })
26 |
27 | test('should be able to display user name without api response', () => {
28 | const wrapper = shallow()
29 |
30 | const header = wrapper.find(WidgetHeader)
31 | expect(header.exists()).toBeTruthy()
32 | expect(header.prop('subject')).toBe(sampleUser)
33 | })
34 |
35 | test('should allow title override', () => {
36 | const wrapper = shallow()
37 |
38 | const header = wrapper.find(WidgetHeader)
39 | expect(header.exists()).toBeTruthy()
40 | expect(header.prop('title')).toBe('override')
41 | expect(header.prop('subject')).toBe(null)
42 | })
43 |
44 | test('should render as expected', () => {
45 | const tree = renderer.create(
46 |
47 |
58 |
59 | )
60 |
61 | expect(tree).toMatchSnapshot()
62 | })
63 |
--------------------------------------------------------------------------------
/test/components/badges/__snapshots__/OrgBadge.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`should render as expected 1`] = `
4 |
8 |
11 |
15 |
16 |
19 | github
20 |
21 | organization
22 |
23 |
42 |
43 |
47 |
50 |
63 |
64 |
65 |
66 |
67 | `;
68 |
--------------------------------------------------------------------------------
/test/components/badges/__snapshots__/UserBadge.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`should render as expected 1`] = `
4 |
8 |
11 |
15 |
16 |
19 | plouc
20 |
21 | GitHub User
22 |
23 |
42 |
43 |
47 |
59 |
90 |
99 |
108 |
111 | 10
112 |
113 |
116 |
121 | public repos
122 |
123 |
124 |
125 |
134 |
137 | 11
138 |
139 |
142 | public gists
143 |
144 |
145 |
154 |
157 | 12
158 |
159 |
162 |
167 | followers
168 |
169 |
170 |
171 |
180 |
183 | 13
184 |
185 |
188 |
193 | following
194 |
195 |
196 |
197 |
205 |
208 | company
209 |
210 |
213 | ploucorp
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | `;
222 |
--------------------------------------------------------------------------------
/test/components/pull-requests/PullRequests.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow } from 'enzyme'
3 | import renderer from 'react-test-renderer'
4 | import { ThemeProvider } from 'styled-components'
5 | import { WidgetHeader, WidgetLoader, defaultTheme } from '@mozaik/ui'
6 | import PullRequests from './../../../src/components/pull-requests/PullRequests'
7 |
8 | const sampleRepository = 'plouc/mozaik'
9 | const samplePullRequests = [
10 | {
11 | id: 0,
12 | title: 'PR-0',
13 | html_url: 'https://github.com/whatever',
14 | created_at: '2011-10-10T14:48:00',
15 | user: {
16 | avatar_url: 'http://mozaik.rocks/avatar.gif',
17 | html_url: 'https://github.com/whatever',
18 | login: 'plouc',
19 | },
20 | },
21 | {
22 | id: 1,
23 | title: 'PR-1',
24 | html_url: 'https://github.com/whatever',
25 | created_at: '2011-10-10T14:48:00',
26 | user: {
27 | avatar_url: 'http://mozaik.rocks/avatar.gif',
28 | html_url: 'https://github.com/whatever',
29 | login: 'john',
30 | },
31 | },
32 | {
33 | id: 2,
34 | title: 'PR-2',
35 | html_url: 'https://github.com/whatever',
36 | created_at: '2011-10-10T14:48:00',
37 | user: {
38 | avatar_url: 'http://mozaik.rocks/avatar.gif',
39 | html_url: 'https://github.com/whatever',
40 | login: 'sarah',
41 | },
42 | },
43 | ]
44 |
45 | test('should return correct api request', () => {
46 | expect(
47 | PullRequests.getApiRequest({
48 | repository: sampleRepository,
49 | })
50 | ).toEqual({
51 | id: `github.pullRequests.${sampleRepository}`,
52 | params: { repository: sampleRepository },
53 | })
54 | })
55 |
56 | test('should display loader if no apiData available', () => {
57 | const wrapper = shallow()
58 |
59 | expect(wrapper.find(WidgetLoader).exists()).toBeTruthy()
60 | })
61 |
62 | test('header should display 0 count by default', () => {
63 | const wrapper = shallow()
64 |
65 | const header = wrapper.find(WidgetHeader)
66 | expect(header.prop('count')).toBe(0)
67 | })
68 |
69 | test('header should display pull request count when api sent data', () => {
70 | const wrapper = shallow(
71 |
75 | )
76 |
77 | const header = wrapper.find(WidgetHeader)
78 | expect(header.exists()).toBeTruthy()
79 | expect(header.prop('count')).toBe(samplePullRequests.length)
80 | })
81 |
82 | test(`header title should default to ' Pull Requests'`, () => {
83 | const wrapper = shallow()
84 |
85 | const header = wrapper.find(WidgetHeader)
86 | expect(header.prop('title')).toBe('Pull Requests')
87 | expect(header.prop('subject')).toBe(sampleRepository)
88 | })
89 |
90 | test(`header title should be overridden when passing 'title' prop`, () => {
91 | const customTitle = 'Custom Title'
92 | const wrapper = shallow()
93 |
94 | const header = wrapper.find(WidgetHeader)
95 | expect(header.prop('title')).toBe(customTitle)
96 | expect(header.prop('subject')).toBe(null)
97 | })
98 |
99 | test('should render as expected', () => {
100 | const tree = renderer.create(
101 |
102 |
106 |
107 | )
108 |
109 | expect(tree).toMatchSnapshot()
110 | })
111 |
--------------------------------------------------------------------------------
/test/components/pull-requests/__snapshots__/PullRequests.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`should render as expected 1`] = `
4 |
8 |
11 |
15 |
16 |
19 | plouc/mozaik
20 |
21 | Pull Requests
22 |
25 | 3
26 |
27 |
28 |
47 |
48 |
52 |
53 |
56 |
59 |
69 |

73 |
74 |
75 |
82 |
104 |
108 |
116 |
141 |
142 | 7 years ago
143 |
144 |
145 |
146 |
147 |
150 |
153 |
163 |

167 |
168 |
169 |
176 |
198 |
202 |
210 |
235 |
236 | 7 years ago
237 |
238 |
239 |
240 |
241 |
244 |
247 |
257 |

261 |
262 |
263 |
270 |
292 |
296 |
304 |
329 |
330 | 7 years ago
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 | `;
340 |
--------------------------------------------------------------------------------
/test/setupTests.js:
--------------------------------------------------------------------------------
1 | import Enzyme from 'enzyme'
2 | import Adapter from 'enzyme-adapter-react-16'
3 |
4 | Enzyme.configure({ adapter: new Adapter() })
5 |
--------------------------------------------------------------------------------