mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Merge pull request #392 from acelaya-forks/feature/orphan-visits-card
Feature/orphan visits card
This commit is contained in:
commit
f35be007c1
5 changed files with 28 additions and 10 deletions
|
@ -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*
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
Loading…
Reference in a new issue