mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 01:20:24 +03:00
Add logic to dynamically exclude bots visits in tags table
This commit is contained in:
parent
8fa61a6301
commit
4ebe23e89f
10 changed files with 35 additions and 20 deletions
|
@ -12,9 +12,10 @@
|
|||
"lint:fix": "npm run lint:css:fix && npm run lint:js:fix",
|
||||
"lint:css:fix": "npm run lint:css -- --fix",
|
||||
"lint:js:fix": "npm run lint:js -- --fix",
|
||||
"types": "tsc",
|
||||
"start": "vite serve --host=0.0.0.0",
|
||||
"preview": "vite preview --host=0.0.0.0",
|
||||
"build": "tsc --noEmit && vite build && node scripts/replace-version.mjs",
|
||||
"build": "npm run types && vite build && node scripts/replace-version.mjs",
|
||||
"build:dist": "npm run build && node scripts/create-dist-file.mjs",
|
||||
"test": "jest --env=jsdom --colors",
|
||||
"test:coverage": "npm run test -- --coverage --coverageReporters=text --coverageReporters=text-summary",
|
||||
|
|
|
@ -21,6 +21,9 @@ export interface ShlinkHealth {
|
|||
interface ShlinkTagsStats {
|
||||
tag: string;
|
||||
shortUrlsCount: number;
|
||||
visitsSummary?: ShlinkVisitsSummary; // Optional only before Shlink 3.5.0
|
||||
|
||||
/** @deprecated */
|
||||
visitsCount: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Message } from '../utils/Message';
|
|||
import { OrderingDropdown } from '../utils/OrderingDropdown';
|
||||
import { Result } from '../utils/Result';
|
||||
import { SearchField } from '../utils/SearchField';
|
||||
import type { NormalizedTag } from './data';
|
||||
import type { SimplifiedTag } from './data';
|
||||
import type { TagsOrder, TagsOrderableFields } from './data/TagsListChildrenProps';
|
||||
import { TAGS_ORDERABLE_FIELDS } from './data/TagsListChildrenProps';
|
||||
import type { TagsList as TagsListState } from './reducers/tagsList';
|
||||
|
@ -31,12 +31,19 @@ export const TagsList = (TagsTable: FC<TagsTableProps>) => boundToMercureHub((
|
|||
) => {
|
||||
const [order, setOrder] = useState<TagsOrder>(settings.tags?.defaultOrdering ?? {});
|
||||
const resolveSortedTags = pipe(
|
||||
() => tagsList.filteredTags.map((tag): NormalizedTag => ({
|
||||
tag,
|
||||
shortUrls: tagsList.stats[tag]?.shortUrlsCount ?? 0,
|
||||
visits: tagsList.stats[tag]?.visitsCount ?? 0,
|
||||
})),
|
||||
(normalizedTags) => sortList<NormalizedTag>(normalizedTags, order),
|
||||
() => tagsList.filteredTags.map((tag): SimplifiedTag => {
|
||||
const theTag = tagsList.stats[tag];
|
||||
const visits = (
|
||||
settings.visits?.excludeBots ? theTag?.visitsSummary?.nonBots : theTag?.visitsSummary?.total
|
||||
) ?? theTag?.visitsCount ?? 0;
|
||||
|
||||
return {
|
||||
tag,
|
||||
visits,
|
||||
shortUrls: theTag?.shortUrlsCount ?? 0,
|
||||
};
|
||||
}),
|
||||
(simplifiedTags) => sortList<SimplifiedTag>(simplifiedTags, order),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -9,11 +9,11 @@ import { DropdownBtnMenu } from '../utils/DropdownBtnMenu';
|
|||
import { useToggle } from '../utils/helpers/hooks';
|
||||
import { prettify } from '../utils/helpers/numbers';
|
||||
import type { ColorGenerator } from '../utils/services/ColorGenerator';
|
||||
import type { NormalizedTag, TagModalProps } from './data';
|
||||
import type { SimplifiedTag, TagModalProps } from './data';
|
||||
import { TagBullet } from './helpers/TagBullet';
|
||||
|
||||
export interface TagsTableRowProps {
|
||||
tag: NormalizedTag;
|
||||
tag: SimplifiedTag;
|
||||
selectedServer: SelectedServer;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { SelectedServer } from '../../servers/data';
|
||||
import type { Order } from '../../utils/helpers/ordering';
|
||||
import type { NormalizedTag } from './index';
|
||||
import type { SimplifiedTag } from './index';
|
||||
|
||||
export const TAGS_ORDERABLE_FIELDS = {
|
||||
tag: 'Tag',
|
||||
|
@ -13,6 +13,6 @@ export type TagsOrderableFields = keyof typeof TAGS_ORDERABLE_FIELDS;
|
|||
export type TagsOrder = Order<TagsOrderableFields>;
|
||||
|
||||
export interface TagsListChildrenProps {
|
||||
sortedTags: NormalizedTag[];
|
||||
sortedTags: SimplifiedTag[];
|
||||
selectedServer: SelectedServer;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import type { ShlinkVisitsSummary } from '../../api/types';
|
||||
|
||||
export interface TagStats {
|
||||
shortUrlsCount: number;
|
||||
visitsCount: number;
|
||||
visitsSummary?: ShlinkVisitsSummary; // Optional only before Shlink 3.5.0
|
||||
}
|
||||
|
||||
export interface TagModalProps {
|
||||
|
@ -9,7 +12,7 @@ export interface TagModalProps {
|
|||
toggle: () => void;
|
||||
}
|
||||
|
||||
export interface NormalizedTag {
|
||||
export interface SimplifiedTag {
|
||||
tag: string;
|
||||
shortUrls: number;
|
||||
visits: number;
|
||||
|
|
|
@ -50,6 +50,7 @@ const increaseVisitsForTags = (tags: TagIncrease[], stats: TagsStatsMap) => tags
|
|||
|
||||
const tagStats = theStats[tag];
|
||||
|
||||
// TODO take into consideration bots, nonBots and total
|
||||
return {
|
||||
...theStats,
|
||||
[tag]: {
|
||||
|
@ -78,12 +79,11 @@ export const listTags = (buildShlinkApiClient: ShlinkApiClientBuilder, force = t
|
|||
}
|
||||
|
||||
const { listTags: shlinkListTags, tagsStats } = buildShlinkApiClient(getState);
|
||||
const { tags, stats = [] }: ShlinkTags = await (
|
||||
const { tags, stats }: ShlinkTags = await (
|
||||
supportedFeatures.tagsStats(selectedServer) ? tagsStats() : shlinkListTags()
|
||||
);
|
||||
const processedStats = stats.reduce<TagsStatsMap>((acc, { tag, shortUrlsCount, visitsCount }) => {
|
||||
acc[tag] = { shortUrlsCount, visitsCount };
|
||||
|
||||
const processedStats = stats.reduce<TagsStatsMap>((acc, { tag, ...rest }) => {
|
||||
acc[tag] = rest;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
|||
bottle.decorator('EditTagModal', connect(['tagEdit'], ['editTag', 'tagEdited']));
|
||||
|
||||
bottle.serviceFactory('TagsTableRow', TagsTableRow, 'DeleteTagConfirmModal', 'EditTagModal', 'ColorGenerator');
|
||||
bottle.decorator('TagsTableRow', connect(['settings']));
|
||||
|
||||
bottle.serviceFactory('TagsTable', TagsTable, 'TagsTableRow');
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
|
|||
|
||||
describe('<TagsList />', () => {
|
||||
const filterTags = jest.fn();
|
||||
const TagsListComp = createTagsList(() => <>TagsTable</>);
|
||||
const TagsListComp = createTagsList(({ sortedTags }) => <>TagsTable ({sortedTags.map((t) => t.visits).join(',')})</>);
|
||||
const setUp = (tagsList: Partial<TagsList>) => renderWithEvents(
|
||||
<TagsListComp
|
||||
{...Mock.all<TagsListProps>()}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { screen } from '@testing-library/react';
|
|||
import { useLocation } from 'react-router-dom';
|
||||
import { Mock } from 'ts-mockery';
|
||||
import type { SelectedServer } from '../../src/servers/data';
|
||||
import type { NormalizedTag } from '../../src/tags/data';
|
||||
import type { SimplifiedTag } from '../../src/tags/data';
|
||||
import { TagsTable as createTagsTable } from '../../src/tags/TagsTable';
|
||||
import { rangeOf } from '../../src/utils/utils';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
@ -17,7 +17,7 @@ describe('<TagsTable />', () => {
|
|||
(useLocation as any).mockReturnValue({ search });
|
||||
return renderWithEvents(
|
||||
<TagsTable
|
||||
sortedTags={sortedTags.map((tag) => Mock.of<NormalizedTag>({ tag }))}
|
||||
sortedTags={sortedTags.map((tag) => Mock.of<SimplifiedTag>({ tag }))}
|
||||
selectedServer={Mock.all<SelectedServer>()}
|
||||
currentOrder={{}}
|
||||
orderByColumn={() => orderByColumn}
|
||||
|
|
Loading…
Reference in a new issue