2018-07-29 19:39:00 +03:00
|
|
|
import React from 'react';
|
2018-07-30 21:31:48 +03:00
|
|
|
import { Doughnut, HorizontalBar } from 'react-chartjs-2';
|
2018-07-30 22:34:06 +03:00
|
|
|
import Moment from 'react-moment';
|
2018-07-29 19:39:00 +03:00
|
|
|
import { connect } from 'react-redux';
|
2018-07-29 20:25:22 +03:00
|
|
|
import { pick } from 'ramda';
|
2018-07-30 22:34:06 +03:00
|
|
|
import { Card, CardBody, CardHeader, UncontrolledTooltip } from 'reactstrap';
|
2018-07-29 20:25:22 +03:00
|
|
|
import { getShortUrlVisits } from './reducers/shortUrlVisits';
|
2018-07-30 21:31:48 +03:00
|
|
|
import VisitsParser from '../visits/services/VisitsParser';
|
2018-07-30 21:52:03 +03:00
|
|
|
import preloader from '@fortawesome/fontawesome-free-solid/faCircleNotch';
|
|
|
|
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
|
2018-07-29 19:39:00 +03:00
|
|
|
|
|
|
|
export class ShortUrlsVisits extends React.Component {
|
2018-07-30 21:31:48 +03:00
|
|
|
state = { startDate: '', endDate: '' };
|
|
|
|
|
2018-07-29 20:25:22 +03:00
|
|
|
componentDidMount() {
|
2018-07-29 19:39:00 +03:00
|
|
|
const { match: { params } } = this.props;
|
2018-07-30 21:31:48 +03:00
|
|
|
this.props.getShortUrlVisits(params.shortCode, this.state);
|
2018-07-29 20:25:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2018-07-30 22:34:06 +03:00
|
|
|
const {
|
|
|
|
match: { params },
|
|
|
|
selectedServer,
|
|
|
|
visitsParser,
|
|
|
|
shortUrlVisits: { visits, loading, error, shortUrl }
|
|
|
|
} = this.props;
|
2018-07-31 21:36:27 +03:00
|
|
|
const colors = [
|
|
|
|
'#97BBCD',
|
|
|
|
'#DCDCDC',
|
|
|
|
'#F7464A',
|
|
|
|
'#46BFBD',
|
|
|
|
'#FDB45C',
|
|
|
|
'#949FB1',
|
|
|
|
'#4D5360'
|
|
|
|
];
|
2018-07-29 20:25:22 +03:00
|
|
|
const serverUrl = selectedServer ? selectedServer.url : '';
|
2018-07-30 22:34:06 +03:00
|
|
|
const shortLink = `${serverUrl}/${params.shortCode}`;
|
2018-07-30 22:12:06 +03:00
|
|
|
const generateGraphData = (stats, label, isBarChart) => ({
|
2018-07-30 21:31:48 +03:00
|
|
|
labels: Object.keys(stats),
|
2018-07-30 22:12:06 +03:00
|
|
|
datasets: [
|
|
|
|
{
|
|
|
|
label,
|
|
|
|
data: Object.values(stats),
|
2018-07-31 21:36:27 +03:00
|
|
|
backgroundColor: isBarChart ? 'rgba(70, 150, 229, 0.4)' : colors,
|
|
|
|
borderColor: isBarChart ? 'rgba(70, 150, 229, 1)' : 'white',
|
|
|
|
borderWidth: 2
|
2018-07-30 22:12:06 +03:00
|
|
|
}
|
|
|
|
]
|
2018-07-30 21:31:48 +03:00
|
|
|
});
|
2018-07-31 21:36:27 +03:00
|
|
|
const renderGraphCard = (title, stats, isBarChart, label) =>
|
2018-07-30 22:12:06 +03:00
|
|
|
<div className="col-md-6">
|
|
|
|
<Card className="mt-4">
|
|
|
|
<CardHeader>{title}</CardHeader>
|
|
|
|
<CardBody>
|
2018-07-31 21:36:27 +03:00
|
|
|
{!isBarChart && <Doughnut data={generateGraphData(stats, label || title, isBarChart)} options={{
|
|
|
|
legend: {
|
|
|
|
position: 'right'
|
|
|
|
}
|
|
|
|
}} />}
|
|
|
|
{isBarChart && <HorizontalBar data={generateGraphData(stats, label || title, isBarChart)} options={{
|
|
|
|
legend: {
|
|
|
|
display: false
|
|
|
|
}
|
|
|
|
}} />}
|
2018-07-30 22:12:06 +03:00
|
|
|
</CardBody>
|
|
|
|
</Card>
|
|
|
|
</div>;
|
2018-07-30 21:52:03 +03:00
|
|
|
const renderContent = () => {
|
|
|
|
if (loading) {
|
|
|
|
return (
|
|
|
|
<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) {
|
|
|
|
return (
|
|
|
|
<Card className="mt-4" body inverse color="danger">
|
|
|
|
An error occurred while loading visits :(
|
|
|
|
</Card>
|
|
|
|
);
|
|
|
|
}
|
2018-07-30 21:31:48 +03:00
|
|
|
|
2018-07-30 21:52:03 +03:00
|
|
|
return (
|
2018-07-30 21:31:48 +03:00
|
|
|
<div className="row">
|
2018-07-30 22:12:06 +03:00
|
|
|
{renderGraphCard('Operating systems', visitsParser.processOsStats(visits), false)}
|
|
|
|
{renderGraphCard('Browsers', visitsParser.processBrowserStats(visits), false)}
|
2018-07-31 21:36:27 +03:00
|
|
|
{renderGraphCard('Countries', visitsParser.processCountriesStats(visits), true, 'Visits')}
|
|
|
|
{renderGraphCard('Referrers', visitsParser.processReferrersStats(visits), true, 'Visits')}
|
2018-07-29 20:25:22 +03:00
|
|
|
</div>
|
2018-07-30 21:52:03 +03:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2018-07-30 22:34:06 +03:00
|
|
|
const renderCreated = () =>
|
|
|
|
<span>
|
|
|
|
<b id="created"><Moment fromNow>{shortUrl.dateCreated}</Moment></b>
|
|
|
|
<UncontrolledTooltip placement="bottom" target="created">
|
|
|
|
<Moment format="YYYY-MM-DD HH:mm">{shortUrl.dateCreated}</Moment>
|
|
|
|
</UncontrolledTooltip>
|
|
|
|
</span>;
|
2018-07-30 21:52:03 +03:00
|
|
|
return (
|
|
|
|
<div className="short-urls-container">
|
2018-07-30 22:34:06 +03:00
|
|
|
<header>
|
|
|
|
<Card className="bg-light">
|
|
|
|
<CardBody>
|
|
|
|
<h2>
|
|
|
|
{
|
|
|
|
shortUrl.visitsCount &&
|
|
|
|
<span className="badge badge-primary float-right">Visits: {shortUrl.visitsCount}</span>
|
|
|
|
}
|
|
|
|
Visit stats for <a target="_blank" href={shortLink}>{shortLink}</a>
|
|
|
|
</h2>
|
|
|
|
<hr />
|
|
|
|
{shortUrl.dateCreated && <div>
|
|
|
|
Created:
|
|
|
|
|
|
|
|
{loading && <small>Loading...</small>}
|
|
|
|
{!loading && renderCreated()}
|
|
|
|
</div>}
|
|
|
|
<div>
|
|
|
|
Original URL:
|
|
|
|
|
|
|
|
{loading && <small>Loading...</small>}
|
|
|
|
{!loading && <a target="_blank" href={shortUrl.longUrl}>{shortUrl.longUrl}</a>}
|
|
|
|
</div>
|
|
|
|
</CardBody>
|
|
|
|
</Card>
|
|
|
|
</header>
|
2018-07-30 21:52:03 +03:00
|
|
|
|
2018-07-30 22:34:06 +03:00
|
|
|
<section>
|
|
|
|
{renderContent()}
|
|
|
|
</section>
|
2018-07-29 20:25:22 +03:00
|
|
|
</div>
|
|
|
|
);
|
2018-07-29 19:39:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-30 21:31:48 +03:00
|
|
|
ShortUrlsVisits.defaultProps = {
|
|
|
|
visitsParser: VisitsParser
|
|
|
|
};
|
|
|
|
|
|
|
|
export default connect(pick(['selectedServer', 'shortUrlVisits']), {
|
2018-07-29 20:25:22 +03:00
|
|
|
getShortUrlVisits
|
|
|
|
})(ShortUrlsVisits);
|