mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Created button to use when anything needs to be exported
This commit is contained in:
parent
187e26810d
commit
7fd360495b
7 changed files with 57 additions and 35 deletions
|
@ -5,12 +5,12 @@ import { SimpleCard } from '../utils/SimpleCard';
|
|||
import { LabeledFormGroup } from '../utils/forms/LabeledFormGroup';
|
||||
import { DEFAULT_SHORT_URLS_ORDERING, Settings, ShortUrlsListSettings as ShortUrlsSettings } from './reducers/settings';
|
||||
|
||||
interface ShortUrlsListProps {
|
||||
interface ShortUrlsListSettingsProps {
|
||||
settings: Settings;
|
||||
setShortUrlsListSettings: (settings: ShortUrlsSettings) => void;
|
||||
}
|
||||
|
||||
export const ShortUrlsListSettings: FC<ShortUrlsListProps> = (
|
||||
export const ShortUrlsListSettings: FC<ShortUrlsListSettingsProps> = (
|
||||
{ settings: { shortUrlsList }, setShortUrlsListSettings },
|
||||
) => (
|
||||
<SimpleCard title="Short URLs list" className="h-100">
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { FC } from 'react';
|
||||
import { faTags as tagsIcon } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { isEmpty, pipe } from 'ramda';
|
||||
import { parseISO } from 'date-fns';
|
||||
import { Row } from 'reactstrap';
|
||||
import classNames from 'classnames';
|
||||
import SearchField from '../utils/SearchField';
|
||||
import Tag from '../tags/helpers/Tag';
|
||||
import { DateRangeSelector } from '../utils/dates/DateRangeSelector';
|
||||
|
@ -11,16 +14,20 @@ import { DateRange } from '../utils/dates/types';
|
|||
import { supportsAllTagsFiltering } from '../utils/helpers/features';
|
||||
import { SelectedServer } from '../servers/data';
|
||||
import { TooltipToggleSwitch } from '../utils/TooltipToggleSwitch';
|
||||
import { ExportBtn } from '../utils/ExportBtn';
|
||||
import { useShortUrlsQuery } from './helpers/hooks';
|
||||
import './ShortUrlsFilteringBar.scss';
|
||||
|
||||
interface ShortUrlsFilteringProps {
|
||||
export interface ShortUrlsFilteringProps {
|
||||
selectedServer: SelectedServer;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const dateOrNull = (date?: string) => date ? parseISO(date) : null;
|
||||
|
||||
const ShortUrlsFilteringBar = (colorGenerator: ColorGenerator) => ({ selectedServer }: ShortUrlsFilteringProps) => {
|
||||
const ShortUrlsFilteringBar = (colorGenerator: ColorGenerator): FC<ShortUrlsFilteringProps> => (
|
||||
{ selectedServer, className },
|
||||
) => {
|
||||
const [{ search, tags, startDate, endDate, tagsMode = 'any' }, toFirstPage ] = useShortUrlsQuery();
|
||||
const selectedTags = tags?.split(',') ?? [];
|
||||
const setDates = pipe(
|
||||
|
@ -46,23 +53,24 @@ const ShortUrlsFilteringBar = (colorGenerator: ColorGenerator) => ({ selectedSer
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="short-urls-filtering-bar-container">
|
||||
<div className={classNames('short-urls-filtering-bar-container', className)}>
|
||||
<SearchField initialValue={search} onChange={setSearch} />
|
||||
|
||||
<div className="mt-3">
|
||||
<div className="row">
|
||||
<div className="col-lg-8 offset-lg-4 col-xl-6 offset-xl-6">
|
||||
<DateRangeSelector
|
||||
defaultText="All short URLs"
|
||||
initialDateRange={{
|
||||
startDate: dateOrNull(startDate),
|
||||
endDate: dateOrNull(endDate),
|
||||
}}
|
||||
onDatesChange={setDates}
|
||||
/>
|
||||
</div>
|
||||
<Row>
|
||||
<div className="col-lg-4 col-xl-6 mt-3">
|
||||
<ExportBtn className="btn-md-block" amount={4} onClick={() => {}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-8 col-xl-6 mt-3">
|
||||
<DateRangeSelector
|
||||
defaultText="All short URLs"
|
||||
initialDateRange={{
|
||||
startDate: dateOrNull(startDate),
|
||||
endDate: dateOrNull(endDate),
|
||||
}}
|
||||
onDatesChange={setDates}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
|
||||
{selectedTags.length > 0 && (
|
||||
<h4 className="mt-3">
|
||||
|
|
|
@ -15,6 +15,7 @@ import { ShortUrlsTableProps } from './ShortUrlsTable';
|
|||
import Paginator from './Paginator';
|
||||
import { useShortUrlsQuery } from './helpers/hooks';
|
||||
import { ShortUrlsOrderableFields, SHORT_URLS_ORDERABLE_FIELDS } from './data';
|
||||
import { ShortUrlsFilteringProps } from './ShortUrlsFilteringBar';
|
||||
|
||||
interface ShortUrlsListProps {
|
||||
selectedServer: SelectedServer;
|
||||
|
@ -23,12 +24,10 @@ interface ShortUrlsListProps {
|
|||
settings: Settings;
|
||||
}
|
||||
|
||||
const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>, ShortUrlsFilteringBar: FC) => boundToMercureHub(({
|
||||
listShortUrls,
|
||||
shortUrlsList,
|
||||
selectedServer,
|
||||
settings,
|
||||
}: ShortUrlsListProps) => {
|
||||
const ShortUrlsList = (
|
||||
ShortUrlsTable: FC<ShortUrlsTableProps>,
|
||||
ShortUrlsFilteringBar: FC<ShortUrlsFilteringProps>,
|
||||
) => boundToMercureHub(({ listShortUrls, shortUrlsList, selectedServer, settings }: ShortUrlsListProps) => {
|
||||
const serverId = getServerId(selectedServer);
|
||||
const { page } = useParams();
|
||||
const location = useLocation();
|
||||
|
@ -66,7 +65,7 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>, ShortUrlsFilteri
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-3"><ShortUrlsFilteringBar /></div>
|
||||
<ShortUrlsFilteringBar selectedServer={selectedServer} className="mb-3" />
|
||||
<div className="d-block d-lg-none mb-3">
|
||||
<OrderingDropdown items={SHORT_URLS_ORDERABLE_FIELDS} order={actualOrderBy} onChange={handleOrderBy} />
|
||||
</div>
|
||||
|
|
|
@ -50,7 +50,6 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
|||
bottle.decorator('QrCodeModal', connect([ 'selectedServer' ]));
|
||||
|
||||
bottle.serviceFactory('ShortUrlsFilteringBar', ShortUrlsFilteringBar, 'ColorGenerator');
|
||||
bottle.decorator('ShortUrlsFilteringBar', connect([ 'selectedServer' ]));
|
||||
|
||||
// Actions
|
||||
bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient');
|
||||
|
|
17
src/utils/ExportBtn.tsx
Normal file
17
src/utils/ExportBtn.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { FC } from 'react';
|
||||
import { Button } from 'reactstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faFileDownload } from '@fortawesome/free-solid-svg-icons';
|
||||
import { prettify } from './helpers/numbers';
|
||||
|
||||
interface ExportBtnProps {
|
||||
onClick: () => void;
|
||||
amount?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ExportBtn: FC<ExportBtnProps> = ({ onClick, className, amount = 0 }) => (
|
||||
<Button outline color="primary" className={className} onClick={onClick}>
|
||||
<FontAwesomeIcon icon={faFileDownload} /> Export ({prettify(amount)})
|
||||
</Button>
|
||||
);
|
|
@ -2,7 +2,7 @@ import { isEmpty, propEq, values } from 'ramda';
|
|||
import { useState, useEffect, useMemo, FC, useRef } from 'react';
|
||||
import { Button, Progress, Row } from 'reactstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCalendarAlt, faMapMarkedAlt, faList, faChartPie, faFileDownload } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faCalendarAlt, faMapMarkedAlt, faList, faChartPie } from '@fortawesome/free-solid-svg-icons';
|
||||
import { IconDefinition } from '@fortawesome/fontawesome-common-types';
|
||||
import { Route, Routes, Navigate } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
|
@ -16,6 +16,7 @@ import { SelectedServer } from '../servers/data';
|
|||
import { supportsBotVisits } from '../utils/helpers/features';
|
||||
import { prettify } from '../utils/helpers/numbers';
|
||||
import { NavPillItem, NavPills } from '../utils/NavPills';
|
||||
import { ExportBtn } from '../utils/ExportBtn';
|
||||
import LineChartCard from './charts/LineChartCard';
|
||||
import VisitsTable from './VisitsTable';
|
||||
import { NormalizedOrphanVisit, NormalizedVisit, VisitsFilter, VisitsInfo, VisitsParams } from './types';
|
||||
|
@ -308,14 +309,11 @@ const VisitsStats: FC<VisitsStatsProps> = ({
|
|||
>
|
||||
Clear selection {highlightedVisits.length > 0 && <>({prettify(highlightedVisits.length)})</>}
|
||||
</Button>
|
||||
<Button
|
||||
outline
|
||||
color="primary"
|
||||
<ExportBtn
|
||||
className="btn-md-block"
|
||||
amount={normalizedVisits.length}
|
||||
onClick={() => exportCsv(normalizedVisits)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFileDownload} /> Export ({prettify(normalizedVisits.length)})
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { Button, Progress } from 'reactstrap';
|
||||
import { Progress } from 'reactstrap';
|
||||
import { sum } from 'ramda';
|
||||
import { Mock } from 'ts-mockery';
|
||||
import { Route } from 'react-router-dom';
|
||||
|
@ -13,6 +13,7 @@ import { Settings } from '../../src/settings/reducers/settings';
|
|||
import { SelectedServer } from '../../src/servers/data';
|
||||
import { SortableBarChartCard } from '../../src/visits/charts/SortableBarChartCard';
|
||||
import { DoughnutChartCard } from '../../src/visits/charts/DoughnutChartCard';
|
||||
import { ExportBtn } from '../../src/utils/ExportBtn';
|
||||
|
||||
describe('<VisitsStats />', () => {
|
||||
const visits = [ Mock.all<Visit>(), Mock.all<Visit>(), Mock.all<Visit>() ];
|
||||
|
@ -106,7 +107,7 @@ describe('<VisitsStats />', () => {
|
|||
|
||||
it('exports CSV when export btn is clicked', () => {
|
||||
const wrapper = createComponent({ visits });
|
||||
const exportBtn = wrapper.find(Button).last();
|
||||
const exportBtn = wrapper.find(ExportBtn).last();
|
||||
|
||||
expect(exportBtn).toHaveLength(1);
|
||||
exportBtn.simulate('click');
|
||||
|
|
Loading…
Reference in a new issue