Updated source code to react-router 6

This commit is contained in:
Alejandro Celaya
2022-02-06 20:07:18 +01:00
parent 071eaddfd1
commit c6e500ba71
31 changed files with 310 additions and 718 deletions

View File

@@ -1,7 +1,7 @@
import { RouteComponentProps } from 'react-router';
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
import { ShlinkVisitsParams } from '../api/types';
import { Topics } from '../mercure/helpers/Topics';
import { useGoBack } from '../utils/helpers/hooks';
import VisitsStats from './VisitsStats';
import { NormalizedVisit, VisitsInfo, VisitsParams } from './types';
import { VisitsExporter } from './services/VisitsExporter';
@@ -9,21 +9,20 @@ import { CommonVisitsProps } from './types/CommonVisitsProps';
import { toApiParams } from './types/helpers';
import { NonOrphanVisitsHeader } from './NonOrphanVisitsHeader';
export interface NonOrphanVisitsProps extends CommonVisitsProps, RouteComponentProps {
export interface NonOrphanVisitsProps extends CommonVisitsProps {
getNonOrphanVisits: (params?: ShlinkVisitsParams, doIntervalFallback?: boolean) => void;
nonOrphanVisits: VisitsInfo;
cancelGetNonOrphanVisits: () => void;
}
export const NonOrphanVisits = ({ exportVisits }: VisitsExporter) => boundToMercureHub(({
history: { goBack },
match: { url },
getNonOrphanVisits,
nonOrphanVisits,
cancelGetNonOrphanVisits,
settings,
selectedServer,
}: NonOrphanVisitsProps) => {
const goBack = useGoBack();
const exportCsv = (visits: NormalizedVisit[]) => exportVisits('non_orphan_visits.csv', visits);
const loadVisits = (params: VisitsParams, doIntervalFallback?: boolean) =>
getNonOrphanVisits(toApiParams(params), doIntervalFallback);
@@ -33,7 +32,6 @@ export const NonOrphanVisits = ({ exportVisits }: VisitsExporter) => boundToMerc
getVisits={loadVisits}
cancelGetVisits={cancelGetNonOrphanVisits}
visitsInfo={nonOrphanVisits}
baseUrl={url}
settings={settings}
exportCsv={exportCsv}
selectedServer={selectedServer}

View File

@@ -1,7 +1,7 @@
import { RouteComponentProps } from 'react-router';
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
import { ShlinkVisitsParams } from '../api/types';
import { Topics } from '../mercure/helpers/Topics';
import { useGoBack } from '../utils/helpers/hooks';
import VisitsStats from './VisitsStats';
import { OrphanVisitsHeader } from './OrphanVisitsHeader';
import { NormalizedVisit, OrphanVisitType, VisitsInfo, VisitsParams } from './types';
@@ -9,7 +9,7 @@ import { VisitsExporter } from './services/VisitsExporter';
import { CommonVisitsProps } from './types/CommonVisitsProps';
import { toApiParams } from './types/helpers';
export interface OrphanVisitsProps extends CommonVisitsProps, RouteComponentProps {
export interface OrphanVisitsProps extends CommonVisitsProps {
getOrphanVisits: (
params?: ShlinkVisitsParams,
orphanVisitsType?: OrphanVisitType,
@@ -20,14 +20,13 @@ export interface OrphanVisitsProps extends CommonVisitsProps, RouteComponentProp
}
export const OrphanVisits = ({ exportVisits }: VisitsExporter) => boundToMercureHub(({
history: { goBack },
match: { url },
getOrphanVisits,
orphanVisits,
cancelGetOrphanVisits,
settings,
selectedServer,
}: OrphanVisitsProps) => {
const goBack = useGoBack();
const exportCsv = (visits: NormalizedVisit[]) => exportVisits('orphan_visits.csv', visits);
const loadVisits = (params: VisitsParams, doIntervalFallback?: boolean) =>
getOrphanVisits(toApiParams(params), params.filter?.orphanVisitsType, doIntervalFallback);
@@ -37,7 +36,6 @@ export const OrphanVisits = ({ exportVisits }: VisitsExporter) => boundToMercure
getVisits={loadVisits}
cancelGetVisits={cancelGetOrphanVisits}
visitsInfo={orphanVisits}
baseUrl={url}
settings={settings}
exportCsv={exportCsv}
selectedServer={selectedServer}

View File

@@ -1,10 +1,11 @@
import { useEffect } from 'react';
import { RouteComponentProps } from 'react-router';
import { useLocation, useParams } from 'react-router-dom';
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
import { ShlinkVisitsParams } from '../api/types';
import { parseQuery } from '../utils/helpers/query';
import { Topics } from '../mercure/helpers/Topics';
import { ShortUrlDetail } from '../short-urls/reducers/shortUrlDetail';
import { useGoBack } from '../utils/helpers/hooks';
import { ShortUrlVisits as ShortUrlVisitsState } from './reducers/shortUrlVisits';
import ShortUrlVisitsHeader from './ShortUrlVisitsHeader';
import VisitsStats from './VisitsStats';
@@ -13,7 +14,7 @@ import { NormalizedVisit, VisitsParams } from './types';
import { CommonVisitsProps } from './types/CommonVisitsProps';
import { toApiParams } from './types/helpers';
export interface ShortUrlVisitsProps extends CommonVisitsProps, RouteComponentProps<{ shortCode: string }> {
export interface ShortUrlVisitsProps extends CommonVisitsProps {
getShortUrlVisits: (shortCode: string, query?: ShlinkVisitsParams, doIntervalFallback?: boolean) => void;
shortUrlVisits: ShortUrlVisitsState;
getShortUrlDetail: Function;
@@ -22,9 +23,6 @@ export interface ShortUrlVisitsProps extends CommonVisitsProps, RouteComponentPr
}
const ShortUrlVisits = ({ exportVisits }: VisitsExporter) => boundToMercureHub(({
history: { goBack },
match: { params, url },
location: { search },
shortUrlVisits,
shortUrlDetail,
getShortUrlVisits,
@@ -33,7 +31,9 @@ const ShortUrlVisits = ({ exportVisits }: VisitsExporter) => boundToMercureHub((
settings,
selectedServer,
}: ShortUrlVisitsProps) => {
const { shortCode } = params;
const { shortCode = '' } = useParams<{ shortCode: string }>();
const { search } = useLocation();
const goBack = useGoBack();
const { domain } = parseQuery<{ domain?: string }>(search);
const loadVisits = (params: VisitsParams, doIntervalFallback?: boolean) =>
getShortUrlVisits(shortCode, { ...toApiParams(params), domain }, doIntervalFallback);
@@ -51,7 +51,6 @@ const ShortUrlVisits = ({ exportVisits }: VisitsExporter) => boundToMercureHub((
getVisits={loadVisits}
cancelGetVisits={cancelGetShortUrlVisits}
visitsInfo={shortUrlVisits}
baseUrl={url}
domain={domain}
settings={settings}
exportCsv={exportCsv}
@@ -60,6 +59,6 @@ const ShortUrlVisits = ({ exportVisits }: VisitsExporter) => boundToMercureHub((
<ShortUrlVisitsHeader shortUrlDetail={shortUrlDetail} shortUrlVisits={shortUrlVisits} goBack={goBack} />
</VisitsStats>
);
}, ({ match }) => [ Topics.shortUrlVisits(match.params.shortCode) ]);
}, (_, params) => [ Topics.shortUrlVisits(params.shortCode) ]);
export default ShortUrlVisits;

View File

@@ -1,8 +1,9 @@
import { RouteComponentProps } from 'react-router';
import { useParams } from 'react-router-dom';
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
import ColorGenerator from '../utils/services/ColorGenerator';
import { ShlinkVisitsParams } from '../api/types';
import { Topics } from '../mercure/helpers/Topics';
import { useGoBack } from '../utils/helpers/hooks';
import { TagVisits as TagVisitsState } from './reducers/tagVisits';
import TagVisitsHeader from './TagVisitsHeader';
import VisitsStats from './VisitsStats';
@@ -11,22 +12,21 @@ import { NormalizedVisit } from './types';
import { CommonVisitsProps } from './types/CommonVisitsProps';
import { toApiParams } from './types/helpers';
export interface TagVisitsProps extends CommonVisitsProps, RouteComponentProps<{ tag: string }> {
export interface TagVisitsProps extends CommonVisitsProps {
getTagVisits: (tag: string, query?: ShlinkVisitsParams, doIntervalFallback?: boolean) => void;
tagVisits: TagVisitsState;
cancelGetTagVisits: () => void;
}
const TagVisits = (colorGenerator: ColorGenerator, { exportVisits }: VisitsExporter) => boundToMercureHub(({
history: { goBack },
match: { params, url },
getTagVisits,
tagVisits,
cancelGetTagVisits,
settings,
selectedServer,
}: TagVisitsProps) => {
const { tag } = params;
const goBack = useGoBack();
const { tag = '' } = useParams();
const loadVisits = (params: ShlinkVisitsParams, doIntervalFallback?: boolean) =>
getTagVisits(tag, toApiParams(params), doIntervalFallback);
const exportCsv = (visits: NormalizedVisit[]) => exportVisits(`tag_${tag}_visits.csv`, visits);
@@ -36,7 +36,6 @@ const TagVisits = (colorGenerator: ColorGenerator, { exportVisits }: VisitsExpor
getVisits={loadVisits}
cancelGetVisits={cancelGetTagVisits}
visitsInfo={tagVisits}
baseUrl={url}
settings={settings}
exportCsv={exportCsv}
selectedServer={selectedServer}

View File

@@ -4,7 +4,7 @@ import { Button, Card, Nav, NavLink, Progress, Row } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCalendarAlt, faMapMarkedAlt, faList, faChartPie, faFileDownload } from '@fortawesome/free-solid-svg-icons';
import { IconDefinition } from '@fortawesome/fontawesome-common-types';
import { Route, Switch, NavLink as RouterNavLink, Redirect } from 'react-router-dom';
import { Route, Routes, NavLink as RouterNavLink, Navigate } from 'react-router-dom';
import { Location } from 'history';
import classNames from 'classnames';
import { DateRangeSelector } from '../utils/dates/DateRangeSelector';
@@ -33,7 +33,6 @@ export interface VisitsStatsProps {
settings: Settings;
selectedServer: SelectedServer;
cancelGetVisits: () => void;
baseUrl: string;
domain?: string;
exportCsv: (visits: NormalizedVisit[]) => void;
isOrphanVisits?: boolean;
@@ -48,10 +47,10 @@ interface VisitsNavLinkProps {
type Section = 'byTime' | 'byContext' | 'byLocation' | 'list';
const sections: Record<Section, VisitsNavLinkProps> = {
byTime: { title: 'By time', subPath: '', icon: faCalendarAlt },
byContext: { title: 'By context', subPath: '/by-context', icon: faChartPie },
byLocation: { title: 'By location', subPath: '/by-location', icon: faMapMarkedAlt },
list: { title: 'List', subPath: '/list', icon: faList },
byTime: { title: 'By time', subPath: 'by-time', icon: faCalendarAlt },
byContext: { title: 'By context', subPath: 'by-context', icon: faChartPie },
byLocation: { title: 'By location', subPath: 'by-location', icon: faMapMarkedAlt },
list: { title: 'List', subPath: 'list', icon: faList },
};
let selectedBar: string | undefined;
@@ -74,7 +73,6 @@ const VisitsStats: FC<VisitsStatsProps> = ({
visitsInfo,
getVisits,
cancelGetVisits,
baseUrl,
domain,
settings,
exportCsv,
@@ -95,7 +93,7 @@ const VisitsStats: FC<VisitsStatsProps> = ({
const buildSectionUrl = (subPath?: string) => {
const query = domain ? `?domain=${domain}` : '';
return !subPath ? `${baseUrl}${query}` : `${baseUrl}${subPath}${query}`;
return !subPath ? `${query}` : `${subPath}${query}`;
};
const normalizedVisits = useMemo(() => normalizeVisits(visits), [ visits ]);
const { os, browsers, referrers, countries, cities, citiesForMap, visitedUrls } = useMemo(
@@ -166,104 +164,120 @@ const VisitsStats: FC<VisitsStatsProps> = ({
</Nav>
</Card>
<Row>
<Switch>
<Route exact path={baseUrl}>
<div className="col-12 mt-3">
<LineChartCard
title="Visits during time"
visits={normalizedVisits}
highlightedVisits={highlightedVisits}
highlightedLabel={highlightedLabel}
setSelectedVisits={setSelectedVisits}
/>
</div>
</Route>
<Route exact path={`${baseUrl}${sections.byContext.subPath}`}>
<div className={classNames('mt-3 col-lg-6', { 'col-xl-4': !isOrphanVisits })}>
<DoughnutChartCard title="Operating systems" stats={os} />
</div>
<div className={classNames('mt-3 col-lg-6', { 'col-xl-4': !isOrphanVisits })}>
<DoughnutChartCard title="Browsers" stats={browsers} />
</div>
<div className={classNames('mt-3', { 'col-xl-4': !isOrphanVisits, 'col-lg-6': isOrphanVisits })}>
<SortableBarChartCard
title="Referrers"
stats={referrers}
withPagination={false}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'referer')}
highlightedLabel={highlightedLabel}
sortingItems={{
name: 'Referrer name',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('referer')}
/>
</div>
{isOrphanVisits && (
<div className="mt-3 col-lg-6">
<SortableBarChartCard
title="Visited URLs"
stats={visitedUrls}
<Routes>
<Route
path={sections.byTime.subPath}
element={(
<div className="col-12 mt-3">
<LineChartCard
title="Visits during time"
visits={normalizedVisits}
highlightedVisits={highlightedVisits}
highlightedLabel={highlightedLabel}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'visitedUrl')}
sortingItems={{
visitedUrl: 'Visited URL',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('visitedUrl')}
setSelectedVisits={setSelectedVisits}
/>
</div>
)}
</Route>
/>
<Route exact path={`${baseUrl}${sections.byLocation.subPath}`}>
<div className="col-lg-6 mt-3">
<SortableBarChartCard
title="Countries"
stats={countries}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'country')}
highlightedLabel={highlightedLabel}
sortingItems={{
name: 'Country name',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('country')}
/>
</div>
<div className="col-lg-6 mt-3">
<SortableBarChartCard
title="Cities"
stats={cities}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'city')}
highlightedLabel={highlightedLabel}
extraHeaderContent={(activeCities: string[]) =>
mapLocations.length > 0 &&
<OpenMapModalBtn modalTitle="Cities" locations={mapLocations} activeCities={activeCities} />
}
sortingItems={{
name: 'City name',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('city')}
/>
</div>
</Route>
<Route
path={sections.byContext.subPath}
element={(
<>
<div className={classNames('mt-3 col-lg-6', { 'col-xl-4': !isOrphanVisits })}>
<DoughnutChartCard title="Operating systems" stats={os} />
</div>
<div className={classNames('mt-3 col-lg-6', { 'col-xl-4': !isOrphanVisits })}>
<DoughnutChartCard title="Browsers" stats={browsers} />
</div>
<div className={classNames('mt-3', { 'col-xl-4': !isOrphanVisits, 'col-lg-6': isOrphanVisits })}>
<SortableBarChartCard
title="Referrers"
stats={referrers}
withPagination={false}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'referer')}
highlightedLabel={highlightedLabel}
sortingItems={{
name: 'Referrer name',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('referer')}
/>
</div>
{isOrphanVisits && (
<div className="mt-3 col-lg-6">
<SortableBarChartCard
title="Visited URLs"
stats={visitedUrls}
highlightedLabel={highlightedLabel}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'visitedUrl')}
sortingItems={{
visitedUrl: 'Visited URL',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('visitedUrl')}
/>
</div>
)}
</>
)}
/>
<Route exact path={`${baseUrl}${sections.list.subPath}`}>
<div className="col-12">
<VisitsTable
visits={normalizedVisits}
selectedVisits={highlightedVisits}
setSelectedVisits={setSelectedVisits}
isOrphanVisits={isOrphanVisits}
selectedServer={selectedServer}
/>
</div>
</Route>
<Route
path={sections.byLocation.subPath}
element={(
<>
<div className="col-lg-6 mt-3">
<SortableBarChartCard
title="Countries"
stats={countries}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'country')}
highlightedLabel={highlightedLabel}
sortingItems={{
name: 'Country name',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('country')}
/>
</div>
<div className="col-lg-6 mt-3">
<SortableBarChartCard
title="Cities"
stats={cities}
highlightedStats={highlightedVisitsToStats(highlightedVisits, 'city')}
highlightedLabel={highlightedLabel}
extraHeaderContent={(activeCities: string[]) =>
mapLocations.length > 0 &&
<OpenMapModalBtn modalTitle="Cities" locations={mapLocations} activeCities={activeCities} />
}
sortingItems={{
name: 'City name',
amount: 'Visits amount',
}}
onClick={highlightVisitsForProp('city')}
/>
</div>
</>
)}
/>
<Redirect to={baseUrl} />
</Switch>
<Route
path={sections.list.subPath}
element={(
<div className="col-12">
<VisitsTable
visits={normalizedVisits}
selectedVisits={highlightedVisits}
setSelectedVisits={setSelectedVisits}
isOrphanVisits={isOrphanVisits}
selectedServer={selectedServer}
/>
</div>
)}
/>
<Route path="*" element={<Navigate replace to={buildSectionUrl(sections.byTime.subPath)} />} />
</Routes>
</Row>
</>
);