mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-07-21 03:01:51 +00:00
Updated source code to react-router 6
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user