src/short-urls/ShortUrlsList.js   A
last analyzed

Complexity

Total Complexity 7
Complexity/F 0

Size

Lines of Code 179
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 81.08%

Importance

Changes 0
Metric Value
wmc 7
eloc 141
mnd 7
bc 7
fnc 0
dl 0
loc 179
ccs 30
cts 37
cp 0.8108
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import { faCaretDown as caretDownIcon, faCaretUp as caretUpIcon } from '@fortawesome/free-solid-svg-icons';
2
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
import { head, isEmpty, keys, values } from 'ramda';
4
import React, { useState, useEffect } from 'react';
5
import qs from 'qs';
6
import PropTypes from 'prop-types';
7
import { serverType } from '../servers/prop-types';
8
import SortingDropdown from '../utils/SortingDropdown';
9
import { determineOrderDir } from '../utils/utils';
10
import { MercureInfoType } from '../mercure/reducers/mercureInfo';
11
import { useMercureTopicBinding } from '../mercure/helpers';
12
import { shortUrlType } from './reducers/shortUrlsList';
13
import { shortUrlsListParamsType } from './reducers/shortUrlsListParams';
14
import './ShortUrlsList.scss';
15
16 1
export const SORTABLE_FIELDS = {
17
  dateCreated: 'Created at',
18
  shortCode: 'Short URL',
19
  longUrl: 'Long URL',
20
  visits: 'Visits',
21
};
22
23 1
const propTypes = {
24
  listShortUrls: PropTypes.func,
25
  resetShortUrlParams: PropTypes.func,
26
  shortUrlsListParams: shortUrlsListParamsType,
27
  match: PropTypes.object,
28
  location: PropTypes.object,
29
  loading: PropTypes.bool,
30
  error: PropTypes.bool,
31
  shortUrlsList: PropTypes.arrayOf(shortUrlType),
32
  selectedServer: serverType,
33
  createNewVisit: PropTypes.func,
34
  loadMercureInfo: PropTypes.func,
35
  mercureInfo: MercureInfoType,
36
};
37
38
// FIXME Replace with typescript: (ShortUrlsRow component)
39 1
const ShortUrlsList = (ShortUrlsRow) => {
40 1
  const ShortUrlsListComp = ({
41
    listShortUrls,
42
    resetShortUrlParams,
43
    shortUrlsListParams,
44
    match,
45
    location,
46
    loading,
47
    error,
48
    shortUrlsList,
49
    selectedServer,
50
    createNewVisit,
51
    loadMercureInfo,
52
    mercureInfo,
53
  }) => {
54 18
    const { orderBy } = shortUrlsListParams;
55 18
    const [ order, setOrder ] = useState({
56
      orderField: orderBy && head(keys(orderBy)),
57
      orderDir: orderBy && head(values(orderBy)),
58
    });
59 18
    const refreshList = (extraParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams });
60 18
    const handleOrderBy = (orderField, orderDir) => {
61 12
      setOrder({ orderField, orderDir });
62 12
      refreshList({ orderBy: { [orderField]: orderDir } });
63
    };
64 72
    const orderByColumn = (columnName) => () =>
65 12
      handleOrderBy(columnName, determineOrderDir(columnName, order.orderField, order.orderDir));
66 18
    const renderOrderIcon = (field) => {
67 72
      if (order.orderField !== field) {
68 60
        return null;
69
      }
70
71 12
      if (!order.orderDir) {
72 4
        return null;
73
      }
74
75 8
      return (
76
        <FontAwesomeIcon
77
          icon={order.orderDir === 'ASC' ? caretUpIcon : caretDownIcon}
78
          className="short-urls-list__header-icon"
79
        />
80
      );
81
    };
82 18
    const renderShortUrls = () => {
83 18
      if (error) {
84
        return (
85
          <tr>
86
            <td colSpan="6" className="text-center table-danger">Something went wrong while loading short URLs :(</td>
87
          </tr>
88
        );
89
      }
90
91 18
      if (loading) {
92
        return <tr><td colSpan="6" className="text-center">Loading...</td></tr>;
93
      }
94
95 18
      if (!loading && isEmpty(shortUrlsList)) {
96
        return <tr><td colSpan="6" className="text-center">No results found</td></tr>;
97
      }
98
99 18
      return shortUrlsList.map((shortUrl) => (
100 18
        <ShortUrlsRow
101
          key={shortUrl.shortUrl}
102
          shortUrl={shortUrl}
103
          selectedServer={selectedServer}
104
          refreshList={refreshList}
105
          shortUrlsListParams={shortUrlsListParams}
106
        />
107
      ));
108
    };
109
110 18
    useEffect(() => {
111
      const { params } = match;
112
      const query = qs.parse(location.search, { ignoreQueryPrefix: true });
113 2
      const tags = query.tag ? [ query.tag ] : shortUrlsListParams.tags;
114
115
      refreshList({ page: params.page, tags });
116
117
      return resetShortUrlParams;
118
    }, []);
119 18
    useMercureTopicBinding(mercureInfo, 'https://shlink.io/new-visit', createNewVisit, loadMercureInfo);
120
121 18
    return (
122
      <React.Fragment>
123
        <div className="d-block d-md-none mb-3">
124
          <SortingDropdown
125
            items={SORTABLE_FIELDS}
126
            orderField={order.orderField}
127
            orderDir={order.orderDir}
128
            onChange={handleOrderBy}
129
          />
130
        </div>
131
        <table className="table table-striped table-hover">
132
          <thead className="short-urls-list__header">
133
            <tr>
134
              <th
135
                className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
136
                onClick={orderByColumn('dateCreated')}
137
              >
138
                {renderOrderIcon('dateCreated')}
139
                Created at
140
              </th>
141
              <th
142
                className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
143
                onClick={orderByColumn('shortCode')}
144
              >
145
                {renderOrderIcon('shortCode')}
146
                Short URL
147
              </th>
148
              <th
149
                className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
150
                onClick={orderByColumn('longUrl')}
151
              >
152
                {renderOrderIcon('longUrl')}
153
                Long URL
154
              </th>
155
              <th className="short-urls-list__header-cell">Tags</th>
156
              <th
157
                className="short-urls-list__header-cell short-urls-list__header-cell--with-action"
158
                onClick={orderByColumn('visits')}
159
              >
160
                <span className="indivisible">{renderOrderIcon('visits')} Visits</span>
161
              </th>
162
              <th className="short-urls-list__header-cell">&nbsp;</th>
163
            </tr>
164
          </thead>
165
          <tbody>
166
            {renderShortUrls()}
167
          </tbody>
168
        </table>
169
      </React.Fragment>
170
    );
171
  };
172
173 1
  ShortUrlsListComp.propTypes = propTypes;
174
175 1
  return ShortUrlsListComp;
176
};
177
178
export default ShortUrlsList;
179