import React, { useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import Moment from 'react-moment'; import classNames from 'classnames'; import { min, splitEvery } from 'ramda'; import { faCaretDown as caretDownIcon, faCaretUp as caretUpIcon, faCheck as checkIcon, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import SimplePaginator from '../common/SimplePaginator'; import SearchField from '../utils/SearchField'; import { determineOrderDir } from '../utils/utils'; import { prettify } from '../utils/helpers/numbers'; import './VisitsTable.scss'; const NormalizedVisitType = PropTypes.shape({ }); const propTypes = { visits: PropTypes.arrayOf(NormalizedVisitType).isRequired, selectedVisits: PropTypes.arrayOf(NormalizedVisitType), setSelectedVisits: PropTypes.func.isRequired, isSticky: PropTypes.bool, matchMedia: PropTypes.func, }; const PAGE_SIZE = 20; const visitMatchesSearch = ({ browser, os, referer, country, city }, searchTerm) => `${browser} ${os} ${referer} ${country} ${city}`.toLowerCase().includes(searchTerm.toLowerCase()); const searchVisits = (searchTerm, visits) => visits.filter((visit) => visitMatchesSearch(visit, searchTerm)); const sortVisits = ({ field, dir }, visits) => visits.sort((a, b) => { const greaterThan = dir === 'ASC' ? 1 : -1; const smallerThan = dir === 'ASC' ? -1 : 1; return a[field] > b[field] ? greaterThan : smallerThan; }); const calculateVisits = (allVisits, searchTerm, order) => { const filteredVisits = searchTerm ? searchVisits(searchTerm, allVisits) : [ ...allVisits ]; const sortedVisits = order.dir ? sortVisits(order, filteredVisits) : filteredVisits; const total = sortedVisits.length; const visitsGroups = splitEvery(PAGE_SIZE, sortedVisits); return { visitsGroups, total }; }; const VisitsTable = ({ visits, selectedVisits = [], setSelectedVisits, isSticky = false, matchMedia = window.matchMedia, }) => { const headerCellsClass = classNames('visits-table__header-cell', { 'visits-table__sticky': isSticky, }); const matchMobile = () => matchMedia('(max-width: 767px)').matches; const [ isMobileDevice, setIsMobileDevice ] = useState(matchMobile()); const [ searchTerm, setSearchTerm ] = useState(undefined); const [ order, setOrder ] = useState({ field: undefined, dir: undefined }); const resultSet = useMemo(() => calculateVisits(visits, searchTerm, order), [ searchTerm, order ]); const [ page, setPage ] = useState(1); const end = page * PAGE_SIZE; const start = end - PAGE_SIZE; const orderByColumn = (field) => () => setOrder({ field, dir: determineOrderDir(field, order.field, order.dir) }); const renderOrderIcon = (field) => order.dir && order.field === field && ( ); useEffect(() => { const listener = () => setIsMobileDevice(matchMobile()); window.addEventListener('resize', listener); return () => window.removeEventListener('resize', listener); }, []); useEffect(() => { setPage(1); setSelectedVisits([]); }, [ searchTerm ]); return ( {(!resultSet.visitsGroups[page - 1] || resultSet.visitsGroups[page - 1].length === 0) && ( )} {resultSet.visitsGroups[page - 1] && resultSet.visitsGroups[page - 1].map((visit, index) => { const isSelected = selectedVisits.includes(visit); return ( setSelectedVisits( isSelected ? selectedVisits.filter((v) => v !== visit) : [ ...selectedVisits, visit ] )} > ); })} {resultSet.total > PAGE_SIZE && ( )}
setSelectedVisits( selectedVisits.length < resultSet.total ? resultSet.visitsGroups.flat() : [] )} > 0 })} /> Date {renderOrderIcon('date')} Country {renderOrderIcon('country')} City {renderOrderIcon('city')} Browser {renderOrderIcon('browser')} OS {renderOrderIcon('os')} Referrer {renderOrderIcon('referer')}
No visits found with current filtering
{isSelected && } {visit.date} {visit.country} {visit.city} {visit.browser} {visit.os} {visit.referer}
Visits {prettify(start + 1)} to{' '} {prettify(min(end, resultSet.total))} of{' '} {prettify(resultSet.total)}
); }; VisitsTable.propTypes = propTypes; export default VisitsTable;