mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Merge pull request #91 from acelaya/feature/cities-graph
Feature/cities graph
This commit is contained in:
commit
95220b913a
5 changed files with 43 additions and 13 deletions
|
@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
* [#54](https://github.com/shlinkio/shlink-web-client/issues/54) Added stats by city graphic in visits page.
|
||||
|
||||
#### Changed
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ const ShortUrlVisits = ({
|
|||
processOsStats,
|
||||
processBrowserStats,
|
||||
processCountriesStats,
|
||||
processCitiesStats,
|
||||
processReferrersStats,
|
||||
}) => class ShortUrlVisits extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -70,13 +71,23 @@ const ShortUrlVisits = ({
|
|||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<div className="col-xl-4 col-lg-6">
|
||||
<GraphCard title="Operating systems" stats={processOsStats(visits)} />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="col-xl-4 col-lg-6">
|
||||
<GraphCard title="Browsers" stats={processBrowserStats(visits)} />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="col-xl-4">
|
||||
<SortableBarGraph
|
||||
stats={processReferrersStats(visits)}
|
||||
title="Referrers"
|
||||
sortingItems={{
|
||||
name: 'Referrer name',
|
||||
amount: 'Visits amount',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<SortableBarGraph
|
||||
stats={processCountriesStats(visits)}
|
||||
title="Countries"
|
||||
|
@ -86,12 +97,12 @@ const ShortUrlVisits = ({
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="col-lg-6">
|
||||
<SortableBarGraph
|
||||
stats={processReferrersStats(visits)}
|
||||
title="Referrers"
|
||||
stats={processCitiesStats(visits)}
|
||||
title="Cities"
|
||||
sortingItems={{
|
||||
name: 'Referrer name',
|
||||
name: 'City name',
|
||||
amount: 'Visits amount',
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -76,16 +76,20 @@ export const processReferrersStats = (visits) =>
|
|||
visits,
|
||||
);
|
||||
|
||||
export const processCountriesStats = (visits) =>
|
||||
const buildLocationStatsProcessorByProperty = (propertyName) => (visits) =>
|
||||
reduce(
|
||||
(stats, { visitLocation }) => {
|
||||
const notHasCountry = isNil(visitLocation)
|
||||
|| isNil(visitLocation.countryName)
|
||||
|| isEmpty(visitLocation.countryName);
|
||||
const country = notHasCountry ? 'Unknown' : visitLocation.countryName;
|
||||
|| isNil(visitLocation[propertyName])
|
||||
|| isEmpty(visitLocation[propertyName]);
|
||||
const country = notHasCountry ? 'Unknown' : visitLocation[propertyName];
|
||||
|
||||
return assoc(country, (stats[country] || 0) + 1, stats);
|
||||
},
|
||||
{},
|
||||
visits,
|
||||
);
|
||||
|
||||
export const processCountriesStats = buildLocationStatsProcessorByProperty('countryName');
|
||||
|
||||
export const processCitiesStats = buildLocationStatsProcessorByProperty('cityName');
|
||||
|
|
|
@ -23,6 +23,7 @@ describe('<ShortUrlVisits />', () => {
|
|||
processCountriesStats: statsProcessor,
|
||||
processOsStats: statsProcessor,
|
||||
processReferrersStats: statsProcessor,
|
||||
processCitiesStats: statsProcessor,
|
||||
});
|
||||
|
||||
wrapper = shallow(
|
||||
|
@ -74,7 +75,7 @@ describe('<ShortUrlVisits />', () => {
|
|||
const wrapper = createComponent({ loading: false, error: false, visits: [{}, {}, {}] });
|
||||
const graphs = wrapper.find(GraphCard);
|
||||
const sortableBarGraphs = wrapper.find(SortableBarGraph);
|
||||
const expectedGraphsCount = 4;
|
||||
const expectedGraphsCount = 5;
|
||||
|
||||
expect(graphs.length + sortableBarGraphs.length).toEqual(expectedGraphsCount);
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
processBrowserStats,
|
||||
processReferrersStats,
|
||||
processCountriesStats,
|
||||
processCitiesStats,
|
||||
} from '../../../src/visits/services/VisitsParser';
|
||||
|
||||
describe('VisitsParser', () => {
|
||||
|
@ -12,6 +13,7 @@ describe('VisitsParser', () => {
|
|||
referer: 'https://google.com',
|
||||
visitLocation: {
|
||||
countryName: 'Spain',
|
||||
cityName: 'Zaragoza',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -19,6 +21,7 @@ describe('VisitsParser', () => {
|
|||
referer: 'https://google.com',
|
||||
visitLocation: {
|
||||
countryName: 'United States',
|
||||
cityName: 'New York',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -32,6 +35,7 @@ describe('VisitsParser', () => {
|
|||
referer: 'https://m.facebook.com',
|
||||
visitLocation: {
|
||||
countryName: 'Spain',
|
||||
cityName: 'Zaragoza',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -78,4 +82,14 @@ describe('VisitsParser', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('processCitiesStats', () => {
|
||||
it('properly parses cities stats', () => {
|
||||
expect(processCitiesStats(visits)).toEqual({
|
||||
'Zaragoza': 2,
|
||||
'New York': 1,
|
||||
'Unknown': 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue