diff --git a/CHANGELOG.md b/CHANGELOG.md
index b17056d3..29096301 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
+## [Unreleased]
+
+#### Added
+
+* *Nothing*
+
+#### Changed
+
+* *Nothing*
+
+#### Deprecated
+
+* *Nothing*
+
+#### Removed
+
+* *Nothing*
+
+#### Fixed
+
+* [#276](https://github.com/shlinkio/shlink-web-client/issues/276) Fixed default grouping used for visits line chart, making it be dynamic depending on how old the short URL is.
+
+
## 2.5.0 - 2020-05-31
#### Added
diff --git a/src/visits/VisitsStats.js b/src/visits/VisitsStats.js
index 7802c98a..c30b8aa2 100644
--- a/src/visits/VisitsStats.js
+++ b/src/visits/VisitsStats.js
@@ -9,8 +9,8 @@ import DateRangeRow from '../utils/DateRangeRow';
import Message from '../utils/Message';
import { formatDate } from '../utils/helpers/date';
import { useToggle } from '../utils/helpers/hooks';
-import SortableBarGraph from './SortableBarGraph';
-import GraphCard from './GraphCard';
+import SortableBarGraph from './helpers/SortableBarGraph';
+import GraphCard from './helpers/GraphCard';
import LineChartCard from './helpers/LineChartCard';
import VisitsTable from './VisitsTable';
import { VisitsInfoType } from './types';
diff --git a/src/visits/GraphCard.js b/src/visits/helpers/GraphCard.js
similarity index 98%
rename from src/visits/GraphCard.js
rename to src/visits/helpers/GraphCard.js
index 9f06c2b7..155a3d9d 100644
--- a/src/visits/GraphCard.js
+++ b/src/visits/helpers/GraphCard.js
@@ -3,7 +3,7 @@ import { Doughnut, HorizontalBar } from 'react-chartjs-2';
import PropTypes from 'prop-types';
import React from 'react';
import { keys, values } from 'ramda';
-import { fillTheGaps } from '../utils/helpers/visits';
+import { fillTheGaps } from '../../utils/helpers/visits';
import './GraphCard.scss';
const propTypes = {
diff --git a/src/visits/GraphCard.scss b/src/visits/helpers/GraphCard.scss
similarity index 100%
rename from src/visits/GraphCard.scss
rename to src/visits/helpers/GraphCard.scss
diff --git a/src/visits/helpers/LineChartCard.js b/src/visits/helpers/LineChartCard.js
index 9da4abe9..d3f3593e 100644
--- a/src/visits/helpers/LineChartCard.js
+++ b/src/visits/helpers/LineChartCard.js
@@ -10,7 +10,7 @@ import {
DropdownItem,
} from 'reactstrap';
import { Line } from 'react-chartjs-2';
-import { reverse } from 'ramda';
+import { always, cond, reverse } from 'ramda';
import moment from 'moment';
import { VisitType } from '../types';
import { fillTheGaps } from '../../utils/helpers/visits';
@@ -52,6 +52,18 @@ const STEP_TO_DATE_FORMAT = {
monthly: (date) => moment(date).format('YYYY-MM'),
};
+const determineInitialStep = (oldestVisitDate) => {
+ const now = moment();
+ const oldestDate = moment(oldestVisitDate);
+ const matcher = cond([
+ [ () => now.diff(oldestDate, 'day') <= 2, always('hourly') ], // Less than 2 days
+ [ () => now.diff(oldestDate, 'month') <= 1, always('daily') ], // Between 2 days and 1 month
+ [ () => now.diff(oldestDate, 'month') <= 6, always('weekly') ], // Between 1 and 6 months
+ ]);
+
+ return matcher() || 'monthly';
+};
+
const groupVisitsByStep = (step, visits) => visits.reduce((acc, visit) => {
const key = STEP_TO_DATE_FORMAT[step](visit.date);
@@ -93,7 +105,9 @@ const generateDataset = (stats, label, color) => ({
});
const LineChartCard = ({ title, visits, highlightedVisits, highlightedLabel = 'Selected' }) => {
- const [ step, setStep ] = useState('monthly');
+ const [ step, setStep ] = useState(
+ visits.length > 0 ? determineInitialStep(visits[visits.length - 1].date) : 'monthly'
+ );
const [ skipNoVisits, toggleSkipNoVisits ] = useToggle(true);
const groupedVisitsWithGaps = useMemo(() => groupVisitsByStep(step, reverse(visits)), [ step, visits ]);
diff --git a/src/visits/SortableBarGraph.js b/src/visits/helpers/SortableBarGraph.js
similarity index 94%
rename from src/visits/SortableBarGraph.js
rename to src/visits/helpers/SortableBarGraph.js
index dc64e71f..efaa03f0 100644
--- a/src/visits/SortableBarGraph.js
+++ b/src/visits/helpers/SortableBarGraph.js
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { fromPairs, head, keys, pipe, prop, reverse, sortBy, splitEvery, toLower, toPairs, type, zipObj } from 'ramda';
-import SortingDropdown from '../utils/SortingDropdown';
-import PaginationDropdown from '../utils/PaginationDropdown';
-import { rangeOf } from '../utils/utils';
-import { roundTen } from '../utils/helpers/numbers';
-import SimplePaginator from '../common/SimplePaginator';
+import SortingDropdown from '../../utils/SortingDropdown';
+import PaginationDropdown from '../../utils/PaginationDropdown';
+import { rangeOf } from '../../utils/utils';
+import { roundTen } from '../../utils/helpers/numbers';
+import SimplePaginator from '../../common/SimplePaginator';
import GraphCard from './GraphCard';
const propTypes = {
diff --git a/test/visits/VisitsStats.test.js b/test/visits/VisitsStats.test.js
index 212127ad..c3872512 100644
--- a/test/visits/VisitsStats.test.js
+++ b/test/visits/VisitsStats.test.js
@@ -4,8 +4,8 @@ import { identity } from 'ramda';
import { Card, Progress } from 'reactstrap';
import createVisitStats from '../../src/visits/VisitsStats';
import Message from '../../src/utils/Message';
-import GraphCard from '../../src/visits/GraphCard';
-import SortableBarGraph from '../../src/visits/SortableBarGraph';
+import GraphCard from '../../src/visits/helpers/GraphCard';
+import SortableBarGraph from '../../src/visits/helpers/SortableBarGraph';
import DateRangeRow from '../../src/utils/DateRangeRow';
describe('', () => {
diff --git a/test/visits/GraphCard.test.js b/test/visits/helpers/GraphCard.test.js
similarity index 97%
rename from test/visits/GraphCard.test.js
rename to test/visits/helpers/GraphCard.test.js
index 25400289..b75ce941 100644
--- a/test/visits/GraphCard.test.js
+++ b/test/visits/helpers/GraphCard.test.js
@@ -2,7 +2,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { Doughnut, HorizontalBar } from 'react-chartjs-2';
import { keys, values } from 'ramda';
-import GraphCard from '../../src/visits/GraphCard';
+import GraphCard from '../../../src/visits/helpers/GraphCard';
describe('', () => {
let wrapper;
diff --git a/test/visits/helpers/LineChartCard.test.js b/test/visits/helpers/LineChartCard.test.js
index ce75e4db..5d692f5b 100644
--- a/test/visits/helpers/LineChartCard.test.js
+++ b/test/visits/helpers/LineChartCard.test.js
@@ -2,6 +2,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { CardHeader, DropdownItem } from 'reactstrap';
import { Line } from 'react-chartjs-2';
+import moment from 'moment';
import LineChartCard from '../../../src/visits/helpers/LineChartCard';
import Checkbox from '../../../src/utils/Checkbox';
@@ -22,19 +23,27 @@ describe('', () => {
expect(header.html()).toContain('Cool title');
});
- it('renders group menu and selects active grouping item', () => {
- const wrapper = createWrapper();
+ it.each([
+ [[], 'monthly' ],
+ [[{ date: moment().subtract(1, 'day').format() }], 'hourly' ],
+ [[{ date: moment().subtract(3, 'day').format() }], 'daily' ],
+ [[{ date: moment().subtract(2, 'month').format() }], 'weekly' ],
+ [[{ date: moment().subtract(6, 'month').format() }], 'weekly' ],
+ [[{ date: moment().subtract(7, 'month').format() }], 'monthly' ],
+ [[{ date: moment().subtract(1, 'year').format() }], 'monthly' ],
+ ])('renders group menu and selects proper grouping item based on visits dates', (visits, expectedActiveItem) => {
+ const wrapper = createWrapper(visits);
const items = wrapper.find(DropdownItem);
expect(items).toHaveLength(4);
expect(items.at(0).prop('children')).toEqual('Month');
- expect(items.at(0).prop('active')).toEqual(true);
+ expect(items.at(0).prop('active')).toEqual(expectedActiveItem === 'monthly');
expect(items.at(1).prop('children')).toEqual('Week');
- expect(items.at(1).prop('active')).toEqual(false);
+ expect(items.at(1).prop('active')).toEqual(expectedActiveItem === 'weekly');
expect(items.at(2).prop('children')).toEqual('Day');
- expect(items.at(2).prop('active')).toEqual(false);
+ expect(items.at(2).prop('active')).toEqual(expectedActiveItem === 'daily');
expect(items.at(3).prop('children')).toEqual('Hour');
- expect(items.at(3).prop('active')).toEqual(false);
+ expect(items.at(3).prop('active')).toEqual(expectedActiveItem === 'hourly');
});
it('renders chart with expected options', () => {
diff --git a/test/visits/SortableBarGraph.test.js b/test/visits/helpers/SortableBarGraph.test.js
similarity index 91%
rename from test/visits/SortableBarGraph.test.js
rename to test/visits/helpers/SortableBarGraph.test.js
index e947d7f8..12f1d09a 100644
--- a/test/visits/SortableBarGraph.test.js
+++ b/test/visits/helpers/SortableBarGraph.test.js
@@ -1,11 +1,11 @@
import React from 'react';
import { shallow } from 'enzyme';
import { keys, range, values } from 'ramda';
-import SortableBarGraph from '../../src/visits/SortableBarGraph';
-import GraphCard from '../../src/visits/GraphCard';
-import SortingDropdown from '../../src/utils/SortingDropdown';
-import PaginationDropdown from '../../src/utils/PaginationDropdown';
-import { rangeOf } from '../../src/utils/utils';
+import SortableBarGraph from '../../../src/visits/helpers/SortableBarGraph';
+import GraphCard from '../../../src/visits/helpers/GraphCard';
+import SortingDropdown from '../../../src/utils/SortingDropdown';
+import PaginationDropdown from '../../../src/utils/PaginationDropdown';
+import { rangeOf } from '../../../src/utils/utils';
describe('', () => {
let wrapper;