Added filtering by date range to visit stats page

This commit is contained in:
Alejandro Celaya 2018-07-31 22:04:20 +02:00
parent a7bd66827a
commit 0b15fba640
4 changed files with 70 additions and 30 deletions

View file

@ -15,7 +15,7 @@ export default class DateInput extends React.Component {
<div className="date-input-container"> <div className="date-input-container">
<DatePicker <DatePicker
{...this.props} {...this.props}
className="date-input-container__input form-control" className={`date-input-container__input form-control ${this.props.className || ''}`}
dateFormat="YYYY-MM-DD" dateFormat="YYYY-MM-DD"
readOnly readOnly
ref={this.inputRef} ref={this.inputRef}

View file

@ -14,6 +14,7 @@ export default class DeleteServerButton extends React.Component {
<span <span
className={this.props.className} className={this.props.className}
onClick={() => this.setState({ isModalOpen: true })} onClick={() => this.setState({ isModalOpen: true })}
key="deleteServerBtn"
> >
<FontAwesomeIcon icon={deleteIcon} /> <FontAwesomeIcon icon={deleteIcon} />
<span className="aside-menu__item-text">Delete this server</span> <span className="aside-menu__item-text">Delete this server</span>
@ -25,6 +26,7 @@ export default class DeleteServerButton extends React.Component {
toggle={() => this.setState({ isModalOpen: !this.state.isModalOpen })} toggle={() => this.setState({ isModalOpen: !this.state.isModalOpen })}
history={history} history={history}
server={server} server={server}
key="deleteServerModal"
/> />
) )
]; ];

View file

@ -1,20 +1,37 @@
import preloader from '@fortawesome/fontawesome-free-solid/faCircleNotch';
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import { isEmpty, mapObjIndexed, pick } from 'ramda';
import React from 'react'; import React from 'react';
import { Doughnut, HorizontalBar } from 'react-chartjs-2'; import { Doughnut, HorizontalBar } from 'react-chartjs-2';
import Moment from 'react-moment'; import Moment from 'react-moment';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { pick } from 'ramda';
import { Card, CardBody, CardHeader, UncontrolledTooltip } from 'reactstrap'; import { Card, CardBody, CardHeader, UncontrolledTooltip } from 'reactstrap';
import { getShortUrlVisits } from './reducers/shortUrlVisits'; import DateInput from '../common/DateInput';
import VisitsParser from '../visits/services/VisitsParser'; import VisitsParser from '../visits/services/VisitsParser';
import preloader from '@fortawesome/fontawesome-free-solid/faCircleNotch'; import { getShortUrlVisits } from './reducers/shortUrlVisits';
import FontAwesomeIcon from '@fortawesome/react-fontawesome'; import './ShortUrlVisits.scss';
const MutedMessage = ({ children }) =>
<div className="col-md-10 offset-md-1">
<Card className="bg-light mt-4" body>
<h3 className="text-center text-muted mb-0">
{children}
</h3>
</Card>
</div>;
export class ShortUrlsVisits extends React.Component { export class ShortUrlsVisits extends React.Component {
state = { startDate: '', endDate: '' }; state = { startDate: undefined, endDate: undefined };
loadVisits = (dates = {}) => {
const { match: { params } } = this.props;
this.props.getShortUrlVisits(params.shortCode, mapObjIndexed(
value => value && value.format ? value.format('YYYY-MM-DD') : value,
{ ...this.state, ...dates }
))
};
componentDidMount() { componentDidMount() {
const { match: { params } } = this.props; this.loadVisits();
this.props.getShortUrlVisits(params.shortCode, this.state);
} }
render() { render() {
@ -24,15 +41,6 @@ export class ShortUrlsVisits extends React.Component {
visitsParser, visitsParser,
shortUrlVisits: { visits, loading, error, shortUrl } shortUrlVisits: { visits, loading, error, shortUrl }
} = this.props; } = this.props;
const colors = [
'#97BBCD',
'#DCDCDC',
'#F7464A',
'#46BFBD',
'#FDB45C',
'#949FB1',
'#4D5360'
];
const serverUrl = selectedServer ? selectedServer.url : ''; const serverUrl = selectedServer ? selectedServer.url : '';
const shortLink = `${serverUrl}/${params.shortCode}`; const shortLink = `${serverUrl}/${params.shortCode}`;
const generateGraphData = (stats, label, isBarChart) => ({ const generateGraphData = (stats, label, isBarChart) => ({
@ -41,7 +49,15 @@ export class ShortUrlsVisits extends React.Component {
{ {
label, label,
data: Object.values(stats), data: Object.values(stats),
backgroundColor: isBarChart ? 'rgba(70, 150, 229, 0.4)' : colors, backgroundColor: isBarChart ? 'rgba(70, 150, 229, 0.4)' : [
'#97BBCD',
'#DCDCDC',
'#F7464A',
'#46BFBD',
'#FDB45C',
'#949FB1',
'#4D5360'
],
borderColor: isBarChart ? 'rgba(70, 150, 229, 1)' : 'white', borderColor: isBarChart ? 'rgba(70, 150, 229, 1)' : 'white',
borderWidth: 2 borderWidth: 2
} }
@ -67,15 +83,7 @@ export class ShortUrlsVisits extends React.Component {
</div>; </div>;
const renderContent = () => { const renderContent = () => {
if (loading) { if (loading) {
return ( return <MutedMessage><FontAwesomeIcon icon={preloader} spin /> Loading...</MutedMessage>;
<div className="col-md-10 offset-md-1">
<Card className="bg-light mt-4" body>
<h3 className="text-center text-muted">
<FontAwesomeIcon icon={preloader} spin /> Loading...
</h3>
</Card>
</div>
);
} }
if (error) { if (error) {
@ -86,6 +94,10 @@ export class ShortUrlsVisits extends React.Component {
); );
} }
if (isEmpty(visits)) {
return <MutedMessage>There have been no visits matching current filter :(</MutedMessage>;
}
return ( return (
<div className="row"> <div className="row">
{renderGraphCard('Operating systems', visitsParser.processOsStats(visits), false)} {renderGraphCard('Operating systems', visitsParser.processOsStats(visits), false)}
@ -132,6 +144,31 @@ export class ShortUrlsVisits extends React.Component {
</Card> </Card>
</header> </header>
<section>
<form onSubmit={e => e.preventDefault()} className="form-inline mt-4 float-md-right">
<label>Period</label>
<DateInput
selected={this.state.startDate}
placeholderText="Since"
onChange={date => {
this.setState({ startDate: date });
this.loadVisits({ startDate: date });
}}
className="short-url-visits__date-input"
/>
<DateInput
selected={this.state.endDate}
placeholderText="Until"
onChange={date => {
this.setState({ endDate: date });
this.loadVisits({ endDate: date });
}}
className="short-url-visits__date-input"
/>
</form>
<div className="clearfix" />
</section>
<section> <section>
{renderContent()} {renderContent()}
</section> </section>
@ -144,6 +181,4 @@ ShortUrlsVisits.defaultProps = {
visitsParser: VisitsParser visitsParser: VisitsParser
}; };
export default connect(pick(['selectedServer', 'shortUrlVisits']), { export default connect(pick(['selectedServer', 'shortUrlVisits']), { getShortUrlVisits })(ShortUrlsVisits);
getShortUrlVisits
})(ShortUrlsVisits);

View file

@ -0,0 +1,3 @@
.short-url-visits__date-input {
margin-left: 10px;
}