Created button to use when anything needs to be exported

This commit is contained in:
Alejandro Celaya 2022-03-12 20:51:30 +01:00
parent 187e26810d
commit 7fd360495b
7 changed files with 57 additions and 35 deletions

View file

@ -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">

View file

@ -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">

View file

@ -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>

View file

@ -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
View 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>
);

View file

@ -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>
)}

View file

@ -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');