Merge pull request #392 from acelaya-forks/feature/orphan-visits-card

Feature/orphan visits card
This commit is contained in:
Alejandro Celaya 2021-02-21 21:04:37 +01:00 committed by GitHub
commit f35be007c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 10 deletions

View file

@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Added ### Added
* [#379](https://github.com/shlinkio/shlink-web-client/issues/379) and [#384](https://github.com/shlinkio/shlink-web-client/issues/384) Improved QR code modal, including controls to customize size, format and margin, as well as a button to copy the link to the clipboard. * [#379](https://github.com/shlinkio/shlink-web-client/issues/379) and [#384](https://github.com/shlinkio/shlink-web-client/issues/384) Improved QR code modal, including controls to customize size, format and margin, as well as a button to copy the link to the clipboard.
* [#385](https://github.com/shlinkio/shlink-web-client/issues/385) Added setting to determine if "validate URL" should be enabled or disabled by default. * [#385](https://github.com/shlinkio/shlink-web-client/issues/385) Added setting to determine if "validate URL" should be enabled or disabled by default.
* [#386](https://github.com/shlinkio/shlink-web-client/issues/386) Added new card in overview section to display amount of orphan visits when using Shlink 2.6.0 or higher.
### Changed ### Changed
* *Nothing* * *Nothing*

View file

@ -46,6 +46,7 @@ export interface ShlinkVisits {
export interface ShlinkVisitsOverview { export interface ShlinkVisitsOverview {
visitsCount: number; visitsCount: number;
orphanVisitsCount?: number; // Optional only for versions older than 2.6.0
} }
export interface ShlinkVisitsParams { export interface ShlinkVisitsParams {

View file

@ -38,7 +38,7 @@ export const Overview = (
}: OverviewConnectProps) => { }: OverviewConnectProps) => {
const { loading, shortUrls } = shortUrlsList; const { loading, shortUrls } = shortUrlsList;
const { loading: loadingTags } = tagsList; const { loading: loadingTags } = tagsList;
const { loading: loadingVisits, visitsCount } = visitsOverview; const { loading: loadingVisits, visitsCount, orphanVisitsCount } = visitsOverview;
const serverId = isServerWithId(selectedServer) ? selectedServer.id : ''; const serverId = isServerWithId(selectedServer) ? selectedServer.id : '';
const history = useHistory(); const history = useHistory();
@ -51,7 +51,7 @@ export const Overview = (
return ( return (
<> <>
<div className="row mb-3"> <div className="row mb-3">
<div className="col-md-6 col-lg-4"> <div className="col-md-6 col-xl-3">
<Card className="overview__card mb-2" body> <Card className="overview__card mb-2" body>
<CardTitle tag="h5" className="overview__card-title">Visits</CardTitle> <CardTitle tag="h5" className="overview__card-title">Visits</CardTitle>
<CardText tag="h2"> <CardText tag="h2">
@ -64,7 +64,20 @@ export const Overview = (
</CardText> </CardText>
</Card> </Card>
</div> </div>
<div className="col-md-6 col-lg-4"> <div className="col-md-6 col-xl-3">
<Card className="overview__card mb-2" body>
<CardTitle tag="h5" className="overview__card-title">Orphan visits</CardTitle>
<CardText tag="h2">
<ForServerVersion minVersion="2.6.0">
{loadingVisits ? 'Loading...' : prettify(orphanVisitsCount ?? 0)}
</ForServerVersion>
<ForServerVersion maxVersion="2.5.*">
<small className="text-muted"><i>Shlink 2.6 is needed</i></small>
</ForServerVersion>
</CardText>
</Card>
</div>
<div className="col-md-6 col-xl-3">
<Card className="overview__card mb-2" body> <Card className="overview__card mb-2" body>
<CardTitle tag="h5" className="overview__card-title">Short URLs</CardTitle> <CardTitle tag="h5" className="overview__card-title">Short URLs</CardTitle>
<CardText tag="h2"> <CardText tag="h2">
@ -72,7 +85,7 @@ export const Overview = (
</CardText> </CardText>
</Card> </Card>
</div> </div>
<div className="col-md-12 col-lg-4"> <div className="col-md-6 col-xl-3">
<Card className="overview__card mb-2" body> <Card className="overview__card mb-2" body>
<CardTitle tag="h5" className="overview__card-title">Tags</CardTitle> <CardTitle tag="h5" className="overview__card-title">Tags</CardTitle>
<CardText tag="h2">{loadingTags ? 'Loading...' : prettify(tagsList.tags.length)}</CardText> <CardText tag="h2">{loadingTags ? 'Loading...' : prettify(tagsList.tags.length)}</CardText>

View file

@ -13,6 +13,7 @@ export const GET_OVERVIEW = 'shlink/visitsOverview/GET_OVERVIEW';
export interface VisitsOverview { export interface VisitsOverview {
visitsCount: number; visitsCount: number;
orphanVisitsCount?: number;
loading: boolean; loading: boolean;
error: boolean; error: boolean;
} }
@ -21,6 +22,7 @@ export type GetVisitsOverviewAction = ShlinkVisitsOverview & Action<string>;
const initialState: VisitsOverview = { const initialState: VisitsOverview = {
visitsCount: 0, visitsCount: 0,
orphanVisitsCount: 0,
loading: false, loading: false,
error: false, error: false,
}; };
@ -28,7 +30,7 @@ const initialState: VisitsOverview = {
export default buildReducer<VisitsOverview, GetVisitsOverviewAction & CreateVisitsAction>({ export default buildReducer<VisitsOverview, GetVisitsOverviewAction & CreateVisitsAction>({
[GET_OVERVIEW_START]: () => ({ ...initialState, loading: true }), [GET_OVERVIEW_START]: () => ({ ...initialState, loading: true }),
[GET_OVERVIEW_ERROR]: () => ({ ...initialState, error: true }), [GET_OVERVIEW_ERROR]: () => ({ ...initialState, error: true }),
[GET_OVERVIEW]: (_, { visitsCount }) => ({ ...initialState, visitsCount }), [GET_OVERVIEW]: (_, { visitsCount, orphanVisitsCount }) => ({ ...initialState, visitsCount, orphanVisitsCount }),
[CREATE_VISITS]: ({ visitsCount, ...rest }, { createdVisits }) => ({ [CREATE_VISITS]: ({ visitsCount, ...rest }, { createdVisits }) => ({
...rest, ...rest,
visitsCount: visitsCount + createdVisits.length, visitsCount: visitsCount + createdVisits.length,

View file

@ -32,7 +32,7 @@ describe('<Overview />', () => {
loadVisitsOverview={loadVisitsOverview} loadVisitsOverview={loadVisitsOverview}
shortUrlsList={Mock.of<ShortUrlsListState>({ loading, shortUrls })} shortUrlsList={Mock.of<ShortUrlsListState>({ loading, shortUrls })}
tagsList={Mock.of<TagsList>({ loading, tags: [ 'foo', 'bar', 'baz' ] })} tagsList={Mock.of<TagsList>({ loading, tags: [ 'foo', 'bar', 'baz' ] })}
visitsOverview={Mock.of<VisitsOverview>({ loading, visitsCount: 3456 })} visitsOverview={Mock.of<VisitsOverview>({ loading, visitsCount: 3456, orphanVisitsCount: 28 })}
selectedServer={Mock.of<ReachableServer>({ id: serverId })} selectedServer={Mock.of<ReachableServer>({ id: serverId })}
createNewVisits={jest.fn()} createNewVisits={jest.fn()}
loadMercureInfo={jest.fn()} loadMercureInfo={jest.fn()}
@ -49,7 +49,7 @@ describe('<Overview />', () => {
const wrapper = createWrapper(true); const wrapper = createWrapper(true);
const cards = wrapper.find(CardText); const cards = wrapper.find(CardText);
expect(cards).toHaveLength(3); expect(cards).toHaveLength(4);
cards.forEach((card) => expect(card.html()).toContain('Loading...')); cards.forEach((card) => expect(card.html()).toContain('Loading...'));
}); });
@ -57,10 +57,11 @@ describe('<Overview />', () => {
const wrapper = createWrapper(); const wrapper = createWrapper();
const cards = wrapper.find(CardText); const cards = wrapper.find(CardText);
expect(cards).toHaveLength(3); expect(cards).toHaveLength(4);
expect(cards.at(0).html()).toContain(prettify(3456)); expect(cards.at(0).html()).toContain(prettify(3456));
expect(cards.at(1).html()).toContain(prettify(83710)); expect(cards.at(1).html()).toContain(prettify(28));
expect(cards.at(2).html()).toContain(prettify(3)); expect(cards.at(2).html()).toContain(prettify(83710));
expect(cards.at(3).html()).toContain(prettify(3));
}); });
test('first card displays warning for old shlink versions', () => { test('first card displays warning for old shlink versions', () => {