Completed
Push — master ( 158ed8...34f194 )
by Alejandro
04:53 queued 02:19
created

src/visits/ShortUrlVisits.js   A

Complexity

Total Complexity 9
Complexity/F 2.25

Size

Lines of Code 172
Function Count 4

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 96.88%

Importance

Changes 0
Metric Value
wmc 9
eloc 145
mnd 5
bc 5
fnc 4
dl 0
loc 172
ccs 31
cts 32
cp 0.9688
rs 10
bpm 1.25
cpm 2.25
noi 0
c 0
b 0
f 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
A ShortUrlVisits.js ➔ componentDidMount 0 8 1
A .loadVisits 0 12 2
C ShortUrlVisits.js ➔ render 0 109 5
A ShortUrlVisits.js ➔ componentWillUnmount 0 3 1
1
import { faCircleNotch as preloader } from '@fortawesome/free-solid-svg-icons';
2
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
import { isEmpty, mapObjIndexed, values } from 'ramda';
4
import React from 'react';
5
import { Card } from 'reactstrap';
6
import PropTypes from 'prop-types';
7
import DateInput from '../utils/DateInput';
8
import MutedMessage from '../utils/MuttedMessage';
9
import SortableBarGraph from './SortableBarGraph';
10
import { shortUrlVisitsType } from './reducers/shortUrlVisits';
11
import VisitsHeader from './VisitsHeader';
12
import GraphCard from './GraphCard';
13
import { shortUrlDetailType } from './reducers/shortUrlDetail';
14
import './ShortUrlVisits.scss';
15
16 1
const ShortUrlVisits = (
17
  { processStatsFromVisits },
18
  OpenMapModalBtn
19 7
) => class ShortUrlVisits extends React.PureComponent {
20
  static propTypes = {
21
    match: PropTypes.shape({
22
      params: PropTypes.object,
23
    }),
24
    getShortUrlVisits: PropTypes.func,
25
    shortUrlVisits: shortUrlVisitsType,
26
    getShortUrlDetail: PropTypes.func,
27
    shortUrlDetail: shortUrlDetailType,
28
    cancelGetShortUrlVisits: PropTypes.func,
29
  };
30
31
  state = { startDate: undefined, endDate: undefined };
32
  loadVisits = () => {
33 10
    const { match: { params }, getShortUrlVisits } = this.props;
34 10
    const { shortCode } = params;
35 10
    const dates = mapObjIndexed(
36 20
      (value) => value && value.format ? value.format('YYYY-MM-DD') : value,
37
      this.state
38
    );
39 10
    const { startDate, endDate } = dates;
40
41
    // While the "page" is loaded, use the timestamp + filtering dates as memoization IDs for stats calcs
42 10
    this.memoizationId = `${this.timeWhenMounted}_${shortCode}_${startDate}_${endDate}`;
43 10
    getShortUrlVisits(shortCode, dates);
44
  };
45
46
  componentDidMount() {
47 7
    const { match: { params }, getShortUrlDetail } = this.props;
48 7
    const { shortCode } = params;
49
50 7
    this.timeWhenMounted = new Date().getTime();
51 7
    this.loadVisits();
52 7
    getShortUrlDetail(shortCode);
53
  }
54
55
  componentWillUnmount() {
56 7
    this.props.cancelGetShortUrlVisits();
57
  }
58
59
  render() {
60 10
    const { shortUrlVisits, shortUrlDetail } = this.props;
61
62 10
    const renderVisitsContent = () => {
63 10
      const { visits, loading, loadingLarge, error } = shortUrlVisits;
64
65 10
      if (loading) {
66 2
        const message = loadingLarge ? 'This is going to take a while... :S' : 'Loading...';
67
68 2
        return <MutedMessage><FontAwesomeIcon icon={preloader} spin /> {message}</MutedMessage>;
69
      }
70
71 8
      if (error) {
72 1
        return (
73
          <Card className="mt-4" body inverse color="danger">
74
            An error occurred while loading visits :(
75
          </Card>
76
        );
77
      }
78
79 7
      if (isEmpty(visits)) {
80 1
        return <MutedMessage>There are no visits matching current filter  :(</MutedMessage>;
81
      }
82
83 6
      const { os, browsers, referrers, countries, cities, citiesForMap } = processStatsFromVisits(
84
        { id: this.memoizationId, visits }
85
      );
86 6
      const mapLocations = values(citiesForMap);
87
88 6
      return (
89
        <div className="row">
90
          <div className="col-xl-4 col-lg-6">
91
            <GraphCard title="Operating systems" stats={os} />
92
          </div>
93
          <div className="col-xl-4 col-lg-6">
94
            <GraphCard title="Browsers" stats={browsers} />
95
          </div>
96
          <div className="col-xl-4">
97
            <SortableBarGraph
98
              stats={referrers}
99
              withPagination={false}
100
              title="Referrers"
101
              sortingItems={{
102
                name: 'Referrer name',
103
                amount: 'Visits amount',
104
              }}
105
            />
106
          </div>
107
          <div className="col-lg-6">
108
            <SortableBarGraph
109
              stats={countries}
110
              title="Countries"
111
              sortingItems={{
112
                name: 'Country name',
113
                amount: 'Visits amount',
114
              }}
115
            />
116
          </div>
117
          <div className="col-lg-6">
118
            <SortableBarGraph
119
              stats={cities}
120
              title="Cities"
121
              extraHeaderContent={(activeCities) =>
122 2
                mapLocations.length > 0 &&
123
                  <OpenMapModalBtn modalTitle="Cities" locations={mapLocations} activeCities={activeCities} />
124
              }
125
              sortingItems={{
126
                name: 'City name',
127
                amount: 'Visits amount',
128
              }}
129
            />
130
          </div>
131
        </div>
132
      );
133
    };
134
135 10
    return (
136
      <div className="shlink-container">
137
        <VisitsHeader shortUrlDetail={shortUrlDetail} shortUrlVisits={shortUrlVisits} />
138
139
        <section className="mt-4">
140
          <div className="row">
141
            <div className="col-xl-3 col-lg-4 col-md-6 offset-xl-6 offset-lg-4">
142
              <DateInput
143
                selected={this.state.startDate}
144
                placeholderText="Since"
145
                isClearable
146
                maxDate={this.state.endDate}
147 3
                onChange={(date) => this.setState({ startDate: date }, this.loadVisits)}
148
              />
149
            </div>
150
            <div className="col-xl-3 col-lg-4 col-md-6">
151
              <DateInput
152
                className="short-url-visits__date-input"
153
                selected={this.state.endDate}
154
                placeholderText="Until"
155
                isClearable
156
                minDate={this.state.startDate}
157
                onChange={(date) => this.setState({ endDate: date }, this.loadVisits)}
158
              />
159
            </div>
160
          </div>
161
        </section>
162
163
        <section>
164
          {renderVisitsContent()}
165
        </section>
166
      </div>
167
    );
168
  }
169
};
170
171
export default ShortUrlVisits;
172