Completed
Push — master ( c670d8...934051 )
by Alejandro
13s queued 11s
created

LineChartCard.js ➔ weekly   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 5
c 0
b 0
f 0
ccs 0
cts 3
cp 0
rs 10
cc 1
crap 2
1
import React, { useState, useMemo } from 'react';
2
import PropTypes from 'prop-types';
3
import {
4
  Card,
5
  CardHeader,
6
  CardBody,
7
  UncontrolledDropdown,
8
  DropdownToggle,
9
  DropdownMenu,
10
  DropdownItem,
11
} from 'reactstrap';
12
import { Line } from 'react-chartjs-2';
13
import { reverse } from 'ramda';
14
import moment from 'moment';
15
import { VisitType } from '../types';
16
import { fillTheGaps } from '../../utils/helpers/visits';
17
import './LineCHartCard.scss';
18
19 2
const propTypes = {
20
  title: PropTypes.string,
21
  visits: PropTypes.arrayOf(VisitType),
22
  highlightedVisits: PropTypes.arrayOf(VisitType),
23
};
24
25 2
const steps = [
26
  { value: 'monthly', menuText: 'Month' },
27
  { value: 'weekly', menuText: 'Week' },
28
  { value: 'daily', menuText: 'Day' },
29
  { value: 'hourly', menuText: 'Hour' },
30
];
31
32 2
const STEP_TO_DATE_FORMAT = {
33
  hourly: (date) => moment(date).format('YYYY-MM-DD HH:00'),
34
  daily: (date) => moment(date).format('YYYY-MM-DD'),
35
  weekly(date) {
36
    const firstWeekDay = moment(date).isoWeekday(1).format('YYYY-MM-DD');
37
    const lastWeekDay = moment(date).isoWeekday(7).format('YYYY-MM-DD');
38
39
    return `${firstWeekDay} - ${lastWeekDay}`;
40
  },
41 3
  monthly: (date) => moment(date).format('YYYY-MM'),
42
};
43
44 10
const groupVisitsByStep = (step, visits) => visits.reduce((acc, visit) => {
45 3
  const key = STEP_TO_DATE_FORMAT[step](visit.date);
46
47 3
  acc[key] = acc[key] ? acc[key] + 1 : 1;
48
49 3
  return acc;
50
}, {});
51
52 6
const generateDataset = (stats, label, color) => ({
53
  label,
54
  data: Object.values(stats),
55
  fill: false,
56
  lineTension: 0.2,
57
  borderColor: color,
58
  backgroundColor: color,
59
});
60
61 2
const LineChartCard = ({ title, visits, highlightedVisits }) => {
62 5
  const [ step, setStep ] = useState(steps[0].value);
63 5
  const groupedVisits = useMemo(() => groupVisitsByStep(step, reverse(visits)), [ visits, step ]);
64 5
  const labels = useMemo(() => Object.keys(groupedVisits), [ groupedVisits ]);
65 5
  const groupedHighlighted = useMemo(
66 5
    () => fillTheGaps(groupVisitsByStep(step, reverse(highlightedVisits)), labels),
67
    [ highlightedVisits, step, labels ]
68
  );
69
70 5
  const data = {
71
    labels,
72
    datasets: [
73
      generateDataset(groupedVisits, 'Visits', '#4696e5'),
74
      highlightedVisits.length > 0 && generateDataset(groupedHighlighted, 'Selected', '#F77F28'),
75
    ].filter(Boolean),
76
  };
77 5
  const options = {
78
    maintainAspectRatio: false,
79
    legend: { display: false },
80
    scales: {
81
      yAxes: [
82
        {
83
          ticks: { beginAtZero: true, precision: 0 },
84
        },
85
      ],
86
    },
87
  };
88
89 5
  return (
90
    <Card>
91
      <CardHeader>
92
        {title}
93
        <div className="float-right">
94
          <UncontrolledDropdown>
95
            <DropdownToggle caret color="link" className="btn-sm p-0">
96
              Group by
97
            </DropdownToggle>
98
            <DropdownMenu right>
99
              {steps.map(({ menuText, value }) => (
100 20
                <DropdownItem key={value} active={step === value} onClick={() => setStep(value)}>
101
                  {menuText}
102
                </DropdownItem>
103
              ))}
104
            </DropdownMenu>
105
          </UncontrolledDropdown>
106
        </div>
107
      </CardHeader>
108
      <CardBody className="line-chart-card__body">
109
        <Line data={data} options={options} />
110
      </CardBody>
111
    </Card>
112
  );
113
};
114
115 2
LineChartCard.propTypes = propTypes;
116
117
export default LineChartCard;
118