Merge pull request #8 from RandellDawson/feat/report-improvements
Feature: Added Pareto Report and Tabs Navigation
This commit is contained in:
committed by
mrugesh mohapatra
parent
e8aa30162a
commit
e31396931e
14974
dashboard-client/app/app/package-lock.json
generated
Normal file
14974
dashboard-client/app/app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import Tabs from './components/Tabs';
|
||||||
import Input from './components/Input';
|
import Input from './components/Input';
|
||||||
import Results from './components/Results';
|
import Results from './components/Results';
|
||||||
|
import Pareto from './components/Pareto';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -12,14 +14,19 @@ const Container = styled.div`
|
|||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 0 4px 0 #777;
|
box-shadow: 0 0 4px 0 #777;
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
state = {
|
state = {
|
||||||
number: '',
|
number: '',
|
||||||
foundPRs: []
|
foundPRs: [],
|
||||||
|
pareto: [],
|
||||||
|
view: 'search'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
clearObj = { number: '', foundPRs: [] };
|
||||||
|
|
||||||
inputRef = React.createRef();
|
inputRef = React.createRef();
|
||||||
|
|
||||||
handleInputEvent = (event) => {
|
handleInputEvent = (event) => {
|
||||||
@ -37,7 +44,6 @@ class App extends Component {
|
|||||||
|
|
||||||
handleButtonClick = () => {
|
handleButtonClick = () => {
|
||||||
const { number } = this.state;
|
const { number } = this.state;
|
||||||
|
|
||||||
this.searchPRs(number);
|
this.searchPRs(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,19 +62,25 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.setState((prevState) => ({ number: '', foundPRs: [] }));
|
this.setState((prevState) => (this.clearObj));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
handleViewChange = ( { target: { id } }) => {
|
||||||
const { handleButtonClick, handleInputEvent, inputRef, state } = this;
|
const view = id.replace('tabs-', '');
|
||||||
const { number, foundPRs } = state;
|
this.setState((prevState) => ({ ...this.clearObj, view }));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { handleButtonClick, handleViewChange, handleInputEvent, inputRef, state } = this;
|
||||||
|
const { number, foundPRs, view } = state;
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
<Tabs view={view} onViewChange={handleViewChange}/>
|
||||||
<Input ref={inputRef} value={number} onInputEvent={handleInputEvent} />
|
<Input ref={inputRef} value={number} onInputEvent={handleInputEvent} />
|
||||||
<button onClick={handleButtonClick}>Search</button>
|
<button onClick={handleButtonClick}>Search</button>
|
||||||
<Results foundPRs={foundPRs} />
|
{ view === 'search' && <Results foundPRs={foundPRs} /> }
|
||||||
|
{ view === 'reports' && <Pareto /> }
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
63
dashboard-client/app/app/src/components/Pareto.js
Normal file
63
dashboard-client/app/app/src/components/Pareto.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Result = styled.div`
|
||||||
|
word-wrap: break-word;
|
||||||
|
margin: 10px 0;
|
||||||
|
&:nth-child(odd) {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
class Pareto extends React.Component {
|
||||||
|
state = {
|
||||||
|
data: []
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
fetch(`https://pr-relations.glitch.me/pareto`)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then(({ ok, pareto }) => {
|
||||||
|
if (ok) {
|
||||||
|
if (!pareto.length) {
|
||||||
|
pareto.push({ filename: 'Nothing to show in Pareto Report', count: 0, prs: [] });
|
||||||
|
}
|
||||||
|
this.setState((prevState) => ({ data: pareto }));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
const pareto = [{ filename: 'Nothing to show in Pareto Report', count: 0, prs: [] }];
|
||||||
|
this.setState((prevState) => ({ data: pareto }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { data } = this.state;
|
||||||
|
|
||||||
|
const elements = data.map((entry) => {
|
||||||
|
const { filename, count, prs } = entry;
|
||||||
|
const prsList = prs.reduce((html, { number, username }) => {
|
||||||
|
const prUrl = `https://github.com/freeCodeCamp/freeCodeCamp/pull/${number}`;
|
||||||
|
return html += `
|
||||||
|
<a href=${prUrl} rel="noopener noreferrer" target="_blank">#${number} <span>${username}</span></a>, `;
|
||||||
|
}, '');
|
||||||
|
return (
|
||||||
|
<Result key={filename}>
|
||||||
|
{filename}<br />
|
||||||
|
<details>
|
||||||
|
<summary># of PRs: {count}</summary>
|
||||||
|
<div dangerouslySetInnerHTML={{__html: prsList}} />
|
||||||
|
</details>
|
||||||
|
</Result>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{elements}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Pareto;
|
32
dashboard-client/app/app/src/components/Tabs.js
Normal file
32
dashboard-client/app/app/src/components/Tabs.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
height: 40px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Tab = styled.div`
|
||||||
|
background: ${({ active }) => active ? 'blue' : 'white'};
|
||||||
|
color: ${({ active }) => active ? 'white' : 'blue'};
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 5px;
|
||||||
|
border: 2px solid blue;
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: blue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Tabs = ({ view, onViewChange }) => {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Tab id="tabs-search" onClick={onViewChange} active={view === 'search'}>Search</Tab>
|
||||||
|
<Tab id="tabs-reports" onClick={onViewChange} active={view === 'reports'}>Reports</Tab>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tabs;
|
@ -6,7 +6,7 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100vh;
|
/* height: 100vh; */
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||||
|
Reference in New Issue
Block a user