mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Extracted cards in overview to their own component
This commit is contained in:
parent
1011b062ae
commit
e0d43020dc
6 changed files with 113 additions and 35 deletions
|
@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
||||||
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
||||||
import { supportsDomainRedirects, supportsOrphanVisits } from '../utils/helpers/features';
|
import { supportsDomainRedirects, supportsNonOrphanVisits, supportsOrphanVisits } from '../utils/helpers/features';
|
||||||
import { isReachableServer } from '../servers/data';
|
import { isReachableServer } from '../servers/data';
|
||||||
import NotFound from './NotFound';
|
import NotFound from './NotFound';
|
||||||
import { AsideMenuProps } from './AsideMenu';
|
import { AsideMenuProps } from './AsideMenu';
|
||||||
|
@ -33,6 +33,7 @@ const MenuLayout = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const addOrphanVisitsRoute = supportsOrphanVisits(selectedServer);
|
const addOrphanVisitsRoute = supportsOrphanVisits(selectedServer);
|
||||||
|
const addNonOrphanVisitsRoute = supportsNonOrphanVisits(selectedServer);
|
||||||
const addManageDomainsRoute = supportsDomainRedirects(selectedServer);
|
const addManageDomainsRoute = supportsDomainRedirects(selectedServer);
|
||||||
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
||||||
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
||||||
|
@ -55,6 +56,7 @@ const MenuLayout = (
|
||||||
<Route path="/server/:serverId/short-code/:shortCode/edit" component={EditShortUrl} />
|
<Route path="/server/:serverId/short-code/:shortCode/edit" component={EditShortUrl} />
|
||||||
<Route path="/server/:serverId/tag/:tag/visits" component={TagVisits} />
|
<Route path="/server/:serverId/tag/:tag/visits" component={TagVisits} />
|
||||||
{addOrphanVisitsRoute && <Route path="/server/:serverId/orphan-visits" component={OrphanVisits} />}
|
{addOrphanVisitsRoute && <Route path="/server/:serverId/orphan-visits" component={OrphanVisits} />}
|
||||||
|
{addNonOrphanVisitsRoute && <Route path="/server/:serverId/visits" render={() => 'Non orphan'} />}
|
||||||
<Route exact path="/server/:serverId/manage-tags" component={TagsList} />
|
<Route exact path="/server/:serverId/manage-tags" component={TagsList} />
|
||||||
{addManageDomainsRoute && <Route exact path="/server/:serverId/manage-domains" component={ManageDomains} />}
|
{addManageDomainsRoute && <Route exact path="/server/:serverId/manage-domains" component={ManageDomains} />}
|
||||||
<Route
|
<Route
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { FC, useEffect } from 'react';
|
import { FC, useEffect } from 'react';
|
||||||
import { Card, CardBody, CardHeader, CardText, CardTitle, Row } from 'reactstrap';
|
import { Card, CardBody, CardHeader, Row } from 'reactstrap';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import { ITEMS_IN_OVERVIEW_PAGE, ShortUrlsList as ShortUrlsListState } from '../short-urls/reducers/shortUrlsList';
|
import { ITEMS_IN_OVERVIEW_PAGE, ShortUrlsList as ShortUrlsListState } from '../short-urls/reducers/shortUrlsList';
|
||||||
import { prettify } from '../utils/helpers/numbers';
|
import { prettify } from '../utils/helpers/numbers';
|
||||||
|
@ -11,8 +11,9 @@ import { VisitsOverview } from '../visits/reducers/visitsOverview';
|
||||||
import { Versions } from '../utils/helpers/version';
|
import { Versions } from '../utils/helpers/version';
|
||||||
import { Topics } from '../mercure/helpers/Topics';
|
import { Topics } from '../mercure/helpers/Topics';
|
||||||
import { ShlinkShortUrlsListParams } from '../api/types';
|
import { ShlinkShortUrlsListParams } from '../api/types';
|
||||||
|
import { supportsNonOrphanVisits, supportsOrphanVisits } from '../utils/helpers/features';
|
||||||
import { getServerId, SelectedServer } from './data';
|
import { getServerId, SelectedServer } from './data';
|
||||||
import './Overview.scss';
|
import { HighlightCard } from './helpers/HighlightCard';
|
||||||
|
|
||||||
interface OverviewConnectProps {
|
interface OverviewConnectProps {
|
||||||
shortUrlsList: ShortUrlsListState;
|
shortUrlsList: ShortUrlsListState;
|
||||||
|
@ -41,6 +42,8 @@ export const Overview = (
|
||||||
const { loading: loadingTags } = tagsList;
|
const { loading: loadingTags } = tagsList;
|
||||||
const { loading: loadingVisits, visitsCount, orphanVisitsCount } = visitsOverview;
|
const { loading: loadingVisits, visitsCount, orphanVisitsCount } = visitsOverview;
|
||||||
const serverId = getServerId(selectedServer);
|
const serverId = getServerId(selectedServer);
|
||||||
|
const linkToOrphanVisits = supportsOrphanVisits(selectedServer);
|
||||||
|
const linkToNonOrphanVisits = supportsNonOrphanVisits(selectedServer);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -52,40 +55,36 @@ export const Overview = (
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row>
|
<Row>
|
||||||
<div className="col-md-6 col-xl-3">
|
<div className="col-lg-6 col-xl-3 mb-3">
|
||||||
<Card className="overview__card mb-3" body>
|
<HighlightCard title="Visits" link={linkToNonOrphanVisits ? `/server/${serverId}/visits` : undefined}>
|
||||||
<CardTitle tag="h5" className="overview__card-title">Visits</CardTitle>
|
{loadingVisits ? 'Loading...' : prettify(visitsCount)}
|
||||||
<CardText tag="h2">{loadingVisits ? 'Loading...' : prettify(visitsCount)}</CardText>
|
</HighlightCard>
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6 col-xl-3">
|
<div className="col-lg-6 col-xl-3 mb-3">
|
||||||
<Card className="overview__card mb-3" body tag={Link} to={`/server/${serverId}/orphan-visits`}>
|
<HighlightCard
|
||||||
<CardTitle tag="h5" className="overview__card-title">Orphan visits</CardTitle>
|
title="Orphan visits"
|
||||||
<CardText tag="h2">
|
link={linkToOrphanVisits ? `/server/${serverId}/orphan-visits` : undefined}
|
||||||
<ForServerVersion minVersion="2.6.0">
|
>
|
||||||
{loadingVisits ? 'Loading...' : prettify(orphanVisitsCount ?? 0)}
|
<ForServerVersion minVersion="2.6.0">
|
||||||
</ForServerVersion>
|
{loadingVisits ? 'Loading...' : prettify(orphanVisitsCount ?? 0)}
|
||||||
<ForServerVersion maxVersion="2.5.*">
|
</ForServerVersion>
|
||||||
<small className="text-muted"><i>Shlink 2.6 is needed</i></small>
|
<ForServerVersion maxVersion="2.5.*">
|
||||||
</ForServerVersion>
|
<small className="text-muted"><i>Shlink 2.6 is needed</i></small>
|
||||||
</CardText>
|
</ForServerVersion>
|
||||||
</Card>
|
</HighlightCard>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6 col-xl-3">
|
<div className="col-lg-6 col-xl-3 mb-3">
|
||||||
<Card className="overview__card mb-3" body tag={Link} to={`/server/${serverId}/list-short-urls/1`}>
|
<HighlightCard title="Short URLs" link={`/server/${serverId}/list-short-urls/1`}>
|
||||||
<CardTitle tag="h5" className="overview__card-title">Short URLs</CardTitle>
|
{loading ? 'Loading...' : prettify(shortUrls?.pagination.totalItems ?? 0)}
|
||||||
<CardText tag="h2">
|
</HighlightCard>
|
||||||
{loading ? 'Loading...' : prettify(shortUrls?.pagination.totalItems ?? 0)}
|
|
||||||
</CardText>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6 col-xl-3">
|
<div className="col-lg-6 col-xl-3 mb-3">
|
||||||
<Card className="overview__card mb-3" body tag={Link} to={`/server/${serverId}/manage-tags`}>
|
<HighlightCard title="Tags" link={`/server/${serverId}/manage-tags`}>
|
||||||
<CardTitle tag="h5" className="overview__card-title">Tags</CardTitle>
|
{loadingTags ? 'Loading...' : prettify(tagsList.tags.length)}
|
||||||
<CardText tag="h2">{loadingTags ? 'Loading...' : prettify(tagsList.tags.length)}</CardText>
|
</HighlightCard>
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Card className="mb-3">
|
<Card className="mb-3">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<span className="d-sm-none">Create a short URL</span>
|
<span className="d-sm-none">Create a short URL</span>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
@import '../utils/base';
|
@import '../../utils/base';
|
||||||
|
|
||||||
.overview__card.overview__card {
|
.highlight-card.highlight-card {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-top: 3px solid var(--brand-color);
|
border-top: 3px solid var(--brand-color);
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overview__card-title {
|
.highlight-card__title {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: $textPlaceholder;
|
color: $textPlaceholder;
|
||||||
}
|
}
|
16
src/servers/helpers/HighlightCard.tsx
Normal file
16
src/servers/helpers/HighlightCard.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { Card, CardText, CardTitle } from 'reactstrap';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import './HighlightCard.scss';
|
||||||
|
|
||||||
|
export interface HighlightCardProps {
|
||||||
|
title: string;
|
||||||
|
link?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HighlightCard: FC<HighlightCardProps> = ({ children, title, link }) => (
|
||||||
|
<Card className="highlight-card" body {...(link && { tag: Link, to: link })}>
|
||||||
|
<CardTitle tag="h5" className="highlight-card__title">{title}</CardTitle>
|
||||||
|
<CardText tag="h2">{children}</CardText>
|
||||||
|
</Card>
|
||||||
|
);
|
|
@ -25,3 +25,5 @@ export const supportsDomainRedirects = supportsQrErrorCorrection;
|
||||||
export const supportsForwardQuery = serverMatchesVersions({ minVersion: '2.9.0' });
|
export const supportsForwardQuery = serverMatchesVersions({ minVersion: '2.9.0' });
|
||||||
|
|
||||||
export const supportsDefaultDomainRedirectsEdition = serverMatchesVersions({ minVersion: '2.10.0' });
|
export const supportsDefaultDomainRedirectsEdition = serverMatchesVersions({ minVersion: '2.10.0' });
|
||||||
|
|
||||||
|
export const supportsNonOrphanVisits = serverMatchesVersions({ minVersion: '3.0.0' });
|
||||||
|
|
59
test/servers/helpers/HighlightCard.test.tsx
Normal file
59
test/servers/helpers/HighlightCard.test.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { Card, CardText, CardTitle } from 'reactstrap';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { HighlightCard, HighlightCardProps } from '../../../src/servers/helpers/HighlightCard';
|
||||||
|
|
||||||
|
describe('<HighlightCard />', () => {
|
||||||
|
let wrapper: ShallowWrapper;
|
||||||
|
const createWrapper = (props: HighlightCardProps & { children?: ReactNode }) => {
|
||||||
|
wrapper = shallow(<HighlightCard {...props} />);
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => wrapper?.unmount());
|
||||||
|
|
||||||
|
it('renders expected components', () => {
|
||||||
|
const wrapper = createWrapper({ title: 'foo' });
|
||||||
|
|
||||||
|
expect(wrapper.find(Card)).toHaveLength(1);
|
||||||
|
expect(wrapper.find(CardTitle)).toHaveLength(1);
|
||||||
|
expect(wrapper.find(CardText)).toHaveLength(1);
|
||||||
|
expect(wrapper.prop('tag')).not.toEqual(Link);
|
||||||
|
expect(wrapper.prop('to')).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[ 'foo' ],
|
||||||
|
[ 'bar' ],
|
||||||
|
[ 'baz' ],
|
||||||
|
])('renders provided title', (title) => {
|
||||||
|
const wrapper = createWrapper({ title });
|
||||||
|
const cardTitle = wrapper.find(CardTitle);
|
||||||
|
|
||||||
|
expect(cardTitle.html()).toContain(`>${title}<`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[ 'foo' ],
|
||||||
|
[ 'bar' ],
|
||||||
|
[ 'baz' ],
|
||||||
|
])('renders provided children', (children) => {
|
||||||
|
const wrapper = createWrapper({ title: 'foo', children });
|
||||||
|
const cardText = wrapper.find(CardText);
|
||||||
|
|
||||||
|
expect(cardText.html()).toContain(`>${children}<`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[ 'foo' ],
|
||||||
|
[ 'bar' ],
|
||||||
|
[ 'baz' ],
|
||||||
|
])('adds extra props when a link is provided', (link) => {
|
||||||
|
const wrapper = createWrapper({ title: 'foo', link });
|
||||||
|
|
||||||
|
expect(wrapper.prop('tag')).toEqual(Link);
|
||||||
|
expect(wrapper.prop('to')).toEqual(link);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue