mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-12-10 14:16:59 -06:00
Created Overview page as default page after connecting to a server
This commit is contained in:
parent
920effb4c6
commit
dba0ac6442
@ -3,6 +3,7 @@ import {
|
|||||||
faLink as createIcon,
|
faLink as createIcon,
|
||||||
faTags as tagsIcon,
|
faTags as tagsIcon,
|
||||||
faPen as editIcon,
|
faPen as editIcon,
|
||||||
|
faHome as overviewIcon,
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
@ -48,6 +49,10 @@ const AsideMenu = (DeleteServerButton: FC<DeleteServerButtonProps>) => (
|
|||||||
return (
|
return (
|
||||||
<aside className={asideClass}>
|
<aside className={asideClass}>
|
||||||
<nav className="nav flex-column aside-menu__nav">
|
<nav className="nav flex-column aside-menu__nav">
|
||||||
|
<AsideMenuItem to={buildPath('/overview')}>
|
||||||
|
<FontAwesomeIcon icon={overviewIcon} />
|
||||||
|
<span className="aside-menu__item-text">Overview</span>
|
||||||
|
</AsideMenuItem>
|
||||||
<AsideMenuItem to={buildPath('/list-short-urls/1')} isActive={shortUrlsIsActive}>
|
<AsideMenuItem to={buildPath('/list-short-urls/1')} isActive={shortUrlsIsActive}>
|
||||||
<FontAwesomeIcon icon={listIcon} />
|
<FontAwesomeIcon icon={listIcon} />
|
||||||
<span className="aside-menu__item-text">List short URLs</span>
|
<span className="aside-menu__item-text">List short URLs</span>
|
||||||
|
|||||||
@ -20,6 +20,7 @@ const MenuLayout = (
|
|||||||
ShortUrlVisits: FC,
|
ShortUrlVisits: FC,
|
||||||
TagVisits: FC,
|
TagVisits: FC,
|
||||||
ServerError: FC,
|
ServerError: FC,
|
||||||
|
Overview: FC,
|
||||||
) => withSelectedServer(({ location, selectedServer }) => {
|
) => withSelectedServer(({ location, selectedServer }) => {
|
||||||
const [ sidebarVisible, toggleSidebar, showSidebar, hideSidebar ] = useToggle();
|
const [ sidebarVisible, toggleSidebar, showSidebar, hideSidebar ] = useToggle();
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ const MenuLayout = (
|
|||||||
<div className="col-lg-10 offset-lg-2 col-md-9 offset-md-3" onClick={() => hideSidebar()}>
|
<div className="col-lg-10 offset-lg-2 col-md-9 offset-md-3" onClick={() => hideSidebar()}>
|
||||||
<div className="menu-layout__container">
|
<div className="menu-layout__container">
|
||||||
<Switch>
|
<Switch>
|
||||||
|
<Route exact path="/server/:serverId/overview" component={Overview} />
|
||||||
<Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrls} />
|
<Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrls} />
|
||||||
<Route exact path="/server/:serverId/create-short-url" component={CreateShortUrl} />
|
<Route exact path="/server/:serverId/create-short-url" component={CreateShortUrl} />
|
||||||
<Route exact path="/server/:serverId/short-code/:shortCode/visits" component={ShortUrlVisits} />
|
<Route exact path="/server/:serverId/short-code/:shortCode/visits" component={ShortUrlVisits} />
|
||||||
|
|||||||
@ -33,6 +33,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter:
|
|||||||
'ShortUrlVisits',
|
'ShortUrlVisits',
|
||||||
'TagVisits',
|
'TagVisits',
|
||||||
'ServerError',
|
'ServerError',
|
||||||
|
'Overview',
|
||||||
);
|
);
|
||||||
bottle.decorator('MenuLayout', connect([ 'selectedServer', 'shortUrlsListParams' ], [ 'selectServer' ]));
|
bottle.decorator('MenuLayout', connect([ 'selectedServer', 'shortUrlsListParams' ], [ 'selectServer' ]));
|
||||||
bottle.decorator('MenuLayout', withRouter);
|
bottle.decorator('MenuLayout', withRouter);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const CreateServer = (ImportServersBtn: FC<ImportServersBtnProps>, useStateFlagT
|
|||||||
const id = uuid();
|
const id = uuid();
|
||||||
|
|
||||||
createServer({ ...serverData, id });
|
createServer({ ...serverData, id });
|
||||||
push(`/server/${id}/list-short-urls/1`);
|
push(`/server/${id}/overview`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -26,7 +26,7 @@ const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }: De
|
|||||||
<p>Are you sure you want to remove <b>{server ? server.name : ''}</b>?</p>
|
<p>Are you sure you want to remove <b>{server ? server.name : ''}</b>?</p>
|
||||||
<p>
|
<p>
|
||||||
<i>
|
<i>
|
||||||
No data will be deleted, only the access to this server will be removed from this host.
|
No data will be deleted, only the access to this server will be removed from this device.
|
||||||
You can create it again at any moment.
|
You can create it again at any moment.
|
||||||
</i>
|
</i>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export const EditServer = (ServerError: FC) => withSelectedServer<EditServerProp
|
|||||||
|
|
||||||
const handleSubmit = (serverData: ServerData) => {
|
const handleSubmit = (serverData: ServerData) => {
|
||||||
editServer(selectedServer.id, serverData);
|
editServer(selectedServer.id, serverData);
|
||||||
push(`/server/${selectedServer.id}/list-short-urls/1`);
|
push(`/server/${selectedServer.id}/overview`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
50
src/servers/Overview.tsx
Normal file
50
src/servers/Overview.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { Card, CardText, CardTitle } from 'reactstrap';
|
||||||
|
import { ShortUrlsListParams } from '../short-urls/reducers/shortUrlsListParams';
|
||||||
|
import { ShortUrlsList as ShortUrlsListState } from '../short-urls/reducers/shortUrlsList';
|
||||||
|
import { prettify } from '../utils/helpers/numbers';
|
||||||
|
import { TagsList } from '../tags/reducers/tagsList';
|
||||||
|
|
||||||
|
interface OverviewConnectProps {
|
||||||
|
shortUrlsList: ShortUrlsListState;
|
||||||
|
listShortUrls: (params: ShortUrlsListParams) => void;
|
||||||
|
listTags: Function;
|
||||||
|
tagsList: TagsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Overview = ({ shortUrlsList, listShortUrls, listTags, tagsList }: OverviewConnectProps) => {
|
||||||
|
const { loading, error, shortUrls } = shortUrlsList;
|
||||||
|
const { loading: loadingTags } = tagsList;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
listShortUrls({ itemsPerPage: 5 });
|
||||||
|
listTags();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-sm-4">
|
||||||
|
<Card className="text-center mb-2 mb-sm-0" body>
|
||||||
|
<CardTitle tag="h5">Visits</CardTitle>
|
||||||
|
<CardText tag="h2">?</CardText>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-4">
|
||||||
|
<Card className="text-center mb-2 mb-sm-0" body>
|
||||||
|
<CardTitle tag="h5">Short URLs</CardTitle>
|
||||||
|
<CardText tag="h2">
|
||||||
|
{loading && !error && 'Loading...'}
|
||||||
|
{error && !loading && 'Failed :('}
|
||||||
|
{!error && !loading && prettify(shortUrls?.pagination.totalItems ?? 0)}
|
||||||
|
</CardText>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-4">
|
||||||
|
<Card className="text-center" body>
|
||||||
|
<CardTitle tag="h5">Tags</CardTitle>
|
||||||
|
<CardText tag="h2">{loadingTags ? 'Loading... ' : prettify(tagsList.tags.length)}</CardText>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -23,7 +23,7 @@ const ServersDropdown = (serversExporter: ServersExporter) => ({ servers, select
|
|||||||
<DropdownItem
|
<DropdownItem
|
||||||
key={id}
|
key={id}
|
||||||
tag={Link}
|
tag={Link}
|
||||||
to={`/server/${id}/list-short-urls/1`}
|
to={`/server/${id}/overview`}
|
||||||
active={isServerWithId(selectedServer) && selectedServer.id === id}
|
active={isServerWithId(selectedServer) && selectedServer.id === id}
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ interface ServersListGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ServerListItem = ({ id, name }: { id: string; name: string }) => (
|
const ServerListItem = ({ id, name }: { id: string; name: string }) => (
|
||||||
<ListGroupItem tag={Link} to={`/server/${id}/list-short-urls/1`} className="servers-list__server-item">
|
<ListGroupItem tag={Link} to={`/server/${id}/overview`} className="servers-list__server-item">
|
||||||
{name}
|
{name}
|
||||||
<FontAwesomeIcon icon={chevronIcon} className="servers-list__server-item-icon" />
|
<FontAwesomeIcon icon={chevronIcon} className="servers-list__server-item-icon" />
|
||||||
</ListGroupItem>
|
</ListGroupItem>
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import ForServerVersion from '../helpers/ForServerVersion';
|
|||||||
import { ServerError } from '../helpers/ServerError';
|
import { ServerError } from '../helpers/ServerError';
|
||||||
import { ConnectDecorator } from '../../container/types';
|
import { ConnectDecorator } from '../../container/types';
|
||||||
import { withoutSelectedServer } from '../helpers/withoutSelectedServer';
|
import { withoutSelectedServer } from '../helpers/withoutSelectedServer';
|
||||||
|
import { Overview } from '../Overview';
|
||||||
import ServersImporter from './ServersImporter';
|
import ServersImporter from './ServersImporter';
|
||||||
import ServersExporter from './ServersExporter';
|
import ServersExporter from './ServersExporter';
|
||||||
|
|
||||||
@ -43,6 +44,12 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter:
|
|||||||
bottle.serviceFactory('ServerError', ServerError, 'DeleteServerButton');
|
bottle.serviceFactory('ServerError', ServerError, 'DeleteServerButton');
|
||||||
bottle.decorator('ServerError', connect([ 'servers', 'selectedServer' ]));
|
bottle.decorator('ServerError', connect([ 'servers', 'selectedServer' ]));
|
||||||
|
|
||||||
|
bottle.serviceFactory('Overview', () => Overview);
|
||||||
|
bottle.decorator('Overview', connect(
|
||||||
|
[ 'shortUrlsList', 'tagsList' ],
|
||||||
|
[ 'listShortUrls', 'listTags' ],
|
||||||
|
));
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
bottle.constant('csvjson', csvjson);
|
bottle.constant('csvjson', csvjson);
|
||||||
bottle.constant('fileReaderFactory', () => new FileReader());
|
bottle.constant('fileReaderFactory', () => new FileReader());
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export type OrderableFields = keyof typeof SORTABLE_FIELDS;
|
|||||||
|
|
||||||
export interface ShortUrlsListParams {
|
export interface ShortUrlsListParams {
|
||||||
page?: string;
|
page?: string;
|
||||||
|
itemsPerPage?: number;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
searchTerm?: string;
|
searchTerm?: string;
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
|
|||||||
@ -36,6 +36,7 @@ export interface ShlinkTagsResponse {
|
|||||||
export interface ShlinkPaginator {
|
export interface ShlinkPaginator {
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
pagesCount: number;
|
pagesCount: number;
|
||||||
|
totalItems: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShlinkVisits {
|
export interface ShlinkVisits {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ describe('<AsideMenu />', () => {
|
|||||||
it('contains links to different sections', () => {
|
it('contains links to different sections', () => {
|
||||||
const links = wrapped.find('[to]');
|
const links = wrapped.find('[to]');
|
||||||
|
|
||||||
expect(links).toHaveLength(4);
|
expect(links).toHaveLength(5);
|
||||||
links.forEach((link) => expect(link.prop('to')).toContain('abc123'));
|
links.forEach((link) => expect(link.prop('to')).toContain('abc123'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user