Added visited URL column on visits table for orphan visits

This commit is contained in:
Alejandro Celaya
2021-03-28 15:57:22 +02:00
parent 205e3ffb90
commit eabd7d9ecb
3 changed files with 39 additions and 12 deletions

View File

@@ -33,6 +33,7 @@ export const OrphanVisits = ({ exportVisits }: VisitsExporter) => boundToMercure
baseUrl={url} baseUrl={url}
settings={settings} settings={settings}
exportCsv={exportCsv} exportCsv={exportCsv}
isOrphanVisits
> >
<OrphanVisitsHeader orphanVisits={orphanVisits} goBack={goBack} /> <OrphanVisitsHeader orphanVisits={orphanVisits} goBack={goBack} />
</VisitsStats> </VisitsStats>

View File

@@ -6,6 +6,7 @@ import { faCalendarAlt, faMapMarkedAlt, faList, faChartPie, faFileDownload } fro
import { IconDefinition } from '@fortawesome/fontawesome-common-types'; import { IconDefinition } from '@fortawesome/fontawesome-common-types';
import { Route, Switch, NavLink as RouterNavLink, Redirect } from 'react-router-dom'; import { Route, Switch, NavLink as RouterNavLink, Redirect } from 'react-router-dom';
import { Location } from 'history'; import { Location } from 'history';
import classNames from 'classnames';
import { DateRangeSelector } from '../utils/dates/DateRangeSelector'; import { DateRangeSelector } from '../utils/dates/DateRangeSelector';
import Message from '../utils/Message'; import Message from '../utils/Message';
import { formatIsoDate } from '../utils/helpers/date'; import { formatIsoDate } from '../utils/helpers/date';
@@ -31,6 +32,7 @@ export interface VisitsStatsProps {
baseUrl: string; baseUrl: string;
domain?: string; domain?: string;
exportCsv: (visits: NormalizedVisit[]) => void; exportCsv: (visits: NormalizedVisit[]) => void;
isOrphanVisits?: boolean;
} }
interface VisitsNavLinkProps { interface VisitsNavLinkProps {
@@ -77,7 +79,7 @@ const VisitsNavLink: FC<VisitsNavLinkProps & { to: string }> = ({ subPath, title
); );
const VisitsStats: FC<VisitsStatsProps> = ( const VisitsStats: FC<VisitsStatsProps> = (
{ children, visitsInfo, getVisits, cancelGetVisits, baseUrl, domain, settings, exportCsv }, { children, visitsInfo, getVisits, cancelGetVisits, baseUrl, domain, settings, exportCsv, isOrphanVisits = false },
) => { ) => {
const initialInterval: DateInterval = settings.visits?.defaultInterval ?? 'last30Days'; const initialInterval: DateInterval = settings.visits?.defaultInterval ?? 'last30Days';
const [ dateRange, setDateRange ] = useState<DateRange>(intervalToDateRange(initialInterval)); const [ dateRange, setDateRange ] = useState<DateRange>(intervalToDateRange(initialInterval));
@@ -171,13 +173,13 @@ const VisitsStats: FC<VisitsStatsProps> = (
</Route> </Route>
<Route exact path={`${baseUrl}${sections.byContext.subPath}`}> <Route exact path={`${baseUrl}${sections.byContext.subPath}`}>
<div className="col-xl-4 col-lg-6 mt-4"> <div className={classNames('mt-4 col-lg-6', { 'col-xl-4': !isOrphanVisits })}>
<GraphCard title="Operating systems" stats={os} /> <GraphCard title="Operating systems" stats={os} />
</div> </div>
<div className="col-xl-4 col-lg-6 mt-4"> <div className={classNames('mt-4 col-lg-6', { 'col-xl-4': !isOrphanVisits })}>
<GraphCard title="Browsers" stats={browsers} /> <GraphCard title="Browsers" stats={browsers} />
</div> </div>
<div className="col-xl-4 mt-4"> <div className={classNames('mt-4', { 'col-xl-4': !isOrphanVisits, 'col-lg-6': isOrphanVisits })}>
<SortableBarGraph <SortableBarGraph
title="Referrers" title="Referrers"
stats={referrers} stats={referrers}
@@ -191,6 +193,18 @@ const VisitsStats: FC<VisitsStatsProps> = (
onClick={highlightVisitsForProp('referer')} onClick={highlightVisitsForProp('referer')}
/> />
</div> </div>
{isOrphanVisits && (
<div className="mt-4 col-lg-6">
<SortableBarGraph
title="Visited URLs"
stats={{}}
sortingItems={{
visitedUrl: 'Visited URL',
amount: 'Visits amount',
}}
/>
</div>
)}
</Route> </Route>
<Route exact path={`${baseUrl}${sections.byLocation.subPath}`}> <Route exact path={`${baseUrl}${sections.byLocation.subPath}`}>
@@ -232,6 +246,7 @@ const VisitsStats: FC<VisitsStatsProps> = (
visits={normalizedVisits} visits={normalizedVisits}
selectedVisits={highlightedVisits} selectedVisits={highlightedVisits}
setSelectedVisits={setSelectedVisits} setSelectedVisits={setSelectedVisits}
isOrphanVisits={isOrphanVisits}
/> />
</div> </div>
</Route> </Route>

View File

@@ -12,7 +12,7 @@ import SimplePaginator from '../common/SimplePaginator';
import SearchField from '../utils/SearchField'; import SearchField from '../utils/SearchField';
import { determineOrderDir, OrderDir } from '../utils/utils'; import { determineOrderDir, OrderDir } from '../utils/utils';
import { prettify } from '../utils/helpers/numbers'; import { prettify } from '../utils/helpers/numbers';
import { NormalizedVisit } from './types'; import { NormalizedOrphanVisit, NormalizedVisit } from './types';
import './VisitsTable.scss'; import './VisitsTable.scss';
interface VisitsTableProps { interface VisitsTableProps {
@@ -20,9 +20,10 @@ interface VisitsTableProps {
selectedVisits?: NormalizedVisit[]; selectedVisits?: NormalizedVisit[];
setSelectedVisits: (visits: NormalizedVisit[]) => void; setSelectedVisits: (visits: NormalizedVisit[]) => void;
matchMedia?: (query: string) => MediaQueryList; matchMedia?: (query: string) => MediaQueryList;
isOrphanVisits?: boolean;
} }
type OrderableFields = 'date' | 'country' | 'city' | 'browser' | 'os' | 'referer'; type OrderableFields = 'date' | 'country' | 'city' | 'browser' | 'os' | 'referer' | 'visitedUrl';
interface Order { interface Order {
field?: OrderableFields; field?: OrderableFields;
@@ -30,8 +31,10 @@ interface Order {
} }
const PAGE_SIZE = 20; const PAGE_SIZE = 20;
const visitMatchesSearch = ({ browser, os, referer, country, city }: NormalizedVisit, searchTerm: string) => const visitMatchesSearch = ({ browser, os, referer, country, city, ...rest }: NormalizedVisit, searchTerm: string) =>
`${browser} ${os} ${referer} ${country} ${city}`.toLowerCase().includes(searchTerm.toLowerCase()); `${browser} ${os} ${referer} ${country} ${city} ${(rest as NormalizedOrphanVisit).visitedUrl}`.toLowerCase().includes(
searchTerm.toLowerCase(),
);
const searchVisits = (searchTerm: string, visits: NormalizedVisit[]) => const searchVisits = (searchTerm: string, visits: NormalizedVisit[]) =>
visits.filter((visit) => visitMatchesSearch(visit, searchTerm)); visits.filter((visit) => visitMatchesSearch(visit, searchTerm));
const sortVisits = ({ field, dir }: Order, visits: NormalizedVisit[]) => !field || !dir ? visits : visits.sort( const sortVisits = ({ field, dir }: Order, visits: NormalizedVisit[]) => !field || !dir ? visits : visits.sort(
@@ -39,7 +42,7 @@ const sortVisits = ({ field, dir }: Order, visits: NormalizedVisit[]) => !field
const greaterThan = dir === 'ASC' ? 1 : -1; const greaterThan = dir === 'ASC' ? 1 : -1;
const smallerThan = dir === 'ASC' ? -1 : 1; const smallerThan = dir === 'ASC' ? -1 : 1;
return a[field] > b[field] ? greaterThan : smallerThan; return (a as NormalizedOrphanVisit)[field] > (b as NormalizedOrphanVisit)[field] ? greaterThan : smallerThan;
}, },
); );
const calculateVisits = (allVisits: NormalizedVisit[], searchTerm: string | undefined, order: Order) => { const calculateVisits = (allVisits: NormalizedVisit[], searchTerm: string | undefined, order: Order) => {
@@ -56,6 +59,7 @@ const VisitsTable = ({
selectedVisits = [], selectedVisits = [],
setSelectedVisits, setSelectedVisits,
matchMedia = window.matchMedia, matchMedia = window.matchMedia,
isOrphanVisits = false,
}: VisitsTableProps) => { }: VisitsTableProps) => {
const headerCellsClass = 'visits-table__header-cell visits-table__sticky'; const headerCellsClass = 'visits-table__header-cell visits-table__sticky';
const matchMobile = () => matchMedia('(max-width: 767px)').matches; const matchMobile = () => matchMedia('(max-width: 767px)').matches;
@@ -132,9 +136,15 @@ const VisitsTable = ({
Referrer Referrer
{renderOrderIcon('referer')} {renderOrderIcon('referer')}
</th> </th>
{isOrphanVisits && (
<th className={headerCellsClass} onClick={orderByColumn('visitedUrl')}>
Visited URL
{renderOrderIcon('visitedUrl')}
</th>
)}
</tr> </tr>
<tr> <tr>
<td colSpan={7} className="p-0"> <td colSpan={isOrphanVisits ? 8 : 7} className="p-0">
<SearchField noBorder large={false} onChange={setSearchTerm} /> <SearchField noBorder large={false} onChange={setSearchTerm} />
</td> </td>
</tr> </tr>
@@ -142,7 +152,7 @@ const VisitsTable = ({
<tbody> <tbody>
{(!resultSet.visitsGroups[page - 1] || resultSet.visitsGroups[page - 1].length === 0) && ( {(!resultSet.visitsGroups[page - 1] || resultSet.visitsGroups[page - 1].length === 0) && (
<tr> <tr>
<td colSpan={7} className="text-center"> <td colSpan={isOrphanVisits ? 8 : 7} className="text-center">
No visits found with current filtering No visits found with current filtering
</td> </td>
</tr> </tr>
@@ -170,6 +180,7 @@ const VisitsTable = ({
<td>{visit.browser}</td> <td>{visit.browser}</td>
<td>{visit.os}</td> <td>{visit.os}</td>
<td>{visit.referer}</td> <td>{visit.referer}</td>
{isOrphanVisits && <td>{(visit as NormalizedOrphanVisit).visitedUrl}</td>}
</tr> </tr>
); );
})} })}
@@ -177,7 +188,7 @@ const VisitsTable = ({
{resultSet.total > PAGE_SIZE && ( {resultSet.total > PAGE_SIZE && (
<tfoot> <tfoot>
<tr> <tr>
<td colSpan={7} className="visits-table__footer-cell visits-table__sticky"> <td colSpan={isOrphanVisits ? 8 : 7} className="visits-table__footer-cell visits-table__sticky">
<div className="row"> <div className="row">
<div className="col-md-6"> <div className="col-md-6">
<SimplePaginator <SimplePaginator