mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-10 18:27:25 +03:00
Added new visits settings
This commit is contained in:
parent
ad46927750
commit
d3f9650e82
8 changed files with 40 additions and 7 deletions
|
@ -3,6 +3,7 @@ import { dissoc, mergeDeepRight } from 'ramda';
|
|||
import { buildReducer } from '../../utils/helpers/redux';
|
||||
import { RecursivePartial } from '../../utils/utils';
|
||||
import { Theme } from '../../utils/theme';
|
||||
import { DateInterval } from '../../utils/dates/types';
|
||||
|
||||
export const SET_SETTINGS = 'shlink/realTimeUpdates/SET_SETTINGS';
|
||||
|
||||
|
@ -24,10 +25,15 @@ export interface UiSettings {
|
|||
theme: Theme;
|
||||
}
|
||||
|
||||
export interface VisitsSettings {
|
||||
defaultInterval: DateInterval;
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
realTimeUpdates: RealTimeUpdatesSettings;
|
||||
shortUrlCreation?: ShortUrlCreationSettings;
|
||||
ui?: UiSettings;
|
||||
visits?: VisitsSettings;
|
||||
}
|
||||
|
||||
const initialState: Settings = {
|
||||
|
@ -40,6 +46,9 @@ const initialState: Settings = {
|
|||
ui: {
|
||||
theme: 'light',
|
||||
},
|
||||
visits: {
|
||||
defaultInterval: 'last30Days',
|
||||
},
|
||||
};
|
||||
|
||||
type SettingsAction = Action & Settings;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { RouteComponentProps } from 'react-router';
|
|||
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
|
||||
import { ShlinkVisitsParams } from '../api/types';
|
||||
import { Topics } from '../mercure/helpers/Topics';
|
||||
import { Settings } from '../settings/reducers/settings';
|
||||
import VisitsStats from './VisitsStats';
|
||||
import { OrphanVisitsHeader } from './OrphanVisitsHeader';
|
||||
import { VisitsInfo } from './types';
|
||||
|
@ -10,6 +11,7 @@ export interface OrphanVisitsProps extends RouteComponentProps {
|
|||
getOrphanVisits: (params: ShlinkVisitsParams) => void;
|
||||
orphanVisits: VisitsInfo;
|
||||
cancelGetOrphanVisits: () => void;
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
export const OrphanVisits = boundToMercureHub(({
|
||||
|
@ -18,12 +20,14 @@ export const OrphanVisits = boundToMercureHub(({
|
|||
getOrphanVisits,
|
||||
orphanVisits,
|
||||
cancelGetOrphanVisits,
|
||||
settings,
|
||||
}: OrphanVisitsProps) => (
|
||||
<VisitsStats
|
||||
getVisits={getOrphanVisits}
|
||||
cancelGetVisits={cancelGetOrphanVisits}
|
||||
visitsInfo={orphanVisits}
|
||||
baseUrl={url}
|
||||
settings={settings}
|
||||
>
|
||||
<OrphanVisitsHeader orphanVisits={orphanVisits} goBack={goBack} />
|
||||
</VisitsStats>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { ShlinkVisitsParams } from '../api/types';
|
|||
import { parseQuery } from '../utils/helpers/query';
|
||||
import { Topics } from '../mercure/helpers/Topics';
|
||||
import { ShortUrlDetail } from '../short-urls/reducers/shortUrlDetail';
|
||||
import { Settings } from '../settings/reducers/settings';
|
||||
import { ShortUrlVisits as ShortUrlVisitsState } from './reducers/shortUrlVisits';
|
||||
import ShortUrlVisitsHeader from './ShortUrlVisitsHeader';
|
||||
import VisitsStats from './VisitsStats';
|
||||
|
@ -15,6 +16,7 @@ export interface ShortUrlVisitsProps extends RouteComponentProps<{ shortCode: st
|
|||
getShortUrlDetail: Function;
|
||||
shortUrlDetail: ShortUrlDetail;
|
||||
cancelGetShortUrlVisits: () => void;
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
const ShortUrlVisits = boundToMercureHub(({
|
||||
|
@ -26,6 +28,7 @@ const ShortUrlVisits = boundToMercureHub(({
|
|||
getShortUrlVisits,
|
||||
getShortUrlDetail,
|
||||
cancelGetShortUrlVisits,
|
||||
settings,
|
||||
}: ShortUrlVisitsProps) => {
|
||||
const { shortCode } = params;
|
||||
const { domain } = parseQuery<{ domain?: string }>(search);
|
||||
|
@ -42,6 +45,7 @@ const ShortUrlVisits = boundToMercureHub(({
|
|||
visitsInfo={shortUrlVisits}
|
||||
baseUrl={url}
|
||||
domain={domain}
|
||||
settings={settings}
|
||||
>
|
||||
<ShortUrlVisitsHeader shortUrlDetail={shortUrlDetail} shortUrlVisits={shortUrlVisits} goBack={goBack} />
|
||||
</VisitsStats>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
|
|||
import ColorGenerator from '../utils/services/ColorGenerator';
|
||||
import { ShlinkVisitsParams } from '../api/types';
|
||||
import { Topics } from '../mercure/helpers/Topics';
|
||||
import { Settings } from '../settings/reducers/settings';
|
||||
import { TagVisits as TagVisitsState } from './reducers/tagVisits';
|
||||
import TagVisitsHeader from './TagVisitsHeader';
|
||||
import VisitsStats from './VisitsStats';
|
||||
|
@ -11,6 +12,7 @@ export interface TagVisitsProps extends RouteComponentProps<{ tag: string }> {
|
|||
getTagVisits: (tag: string, query: any) => void;
|
||||
tagVisits: TagVisitsState;
|
||||
cancelGetTagVisits: () => void;
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
const TagVisits = (colorGenerator: ColorGenerator) => boundToMercureHub(({
|
||||
|
@ -19,12 +21,19 @@ const TagVisits = (colorGenerator: ColorGenerator) => boundToMercureHub(({
|
|||
getTagVisits,
|
||||
tagVisits,
|
||||
cancelGetTagVisits,
|
||||
settings,
|
||||
}: TagVisitsProps) => {
|
||||
const { tag } = params;
|
||||
const loadVisits = (params: ShlinkVisitsParams) => getTagVisits(tag, params);
|
||||
|
||||
return (
|
||||
<VisitsStats getVisits={loadVisits} cancelGetVisits={cancelGetTagVisits} visitsInfo={tagVisits} baseUrl={url}>
|
||||
<VisitsStats
|
||||
getVisits={loadVisits}
|
||||
cancelGetVisits={cancelGetTagVisits}
|
||||
visitsInfo={tagVisits}
|
||||
baseUrl={url}
|
||||
settings={settings}
|
||||
>
|
||||
<TagVisitsHeader tagVisits={tagVisits} goBack={goBack} colorGenerator={colorGenerator} />
|
||||
</VisitsStats>
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@ import { ShlinkVisitsParams } from '../api/types';
|
|||
import { DateInterval, DateRange, intervalToDateRange } from '../utils/dates/types';
|
||||
import { Result } from '../utils/Result';
|
||||
import { ShlinkApiError } from '../api/ShlinkApiError';
|
||||
import { Settings } from '../settings/reducers/settings';
|
||||
import SortableBarGraph from './helpers/SortableBarGraph';
|
||||
import GraphCard from './helpers/GraphCard';
|
||||
import LineChartCard from './helpers/LineChartCard';
|
||||
|
@ -25,6 +26,7 @@ import './VisitsStats.scss';
|
|||
export interface VisitsStatsProps {
|
||||
getVisits: (params: Partial<ShlinkVisitsParams>) => void;
|
||||
visitsInfo: VisitsInfo;
|
||||
settings: Settings;
|
||||
cancelGetVisits: () => void;
|
||||
baseUrl: string;
|
||||
domain?: string;
|
||||
|
@ -59,7 +61,6 @@ const highlightedVisitsToStats = (
|
|||
return acc;
|
||||
}, {});
|
||||
let selectedBar: string | undefined;
|
||||
const initialInterval: DateInterval = 'last30Days';
|
||||
|
||||
const VisitsNavLink: FC<VisitsNavLinkProps & { to: string }> = ({ subPath, title, icon, to }) => (
|
||||
<NavLink
|
||||
|
@ -74,7 +75,10 @@ const VisitsNavLink: FC<VisitsNavLinkProps & { to: string }> = ({ subPath, title
|
|||
</NavLink>
|
||||
);
|
||||
|
||||
const VisitsStats: FC<VisitsStatsProps> = ({ children, visitsInfo, getVisits, cancelGetVisits, baseUrl, domain }) => {
|
||||
const VisitsStats: FC<VisitsStatsProps> = (
|
||||
{ children, visitsInfo, getVisits, cancelGetVisits, baseUrl, domain, settings },
|
||||
) => {
|
||||
const initialInterval: DateInterval = settings.visits?.defaultInterval ?? 'last30Days';
|
||||
const [ dateRange, setDateRange ] = useState<DateRange>(intervalToDateRange(initialInterval));
|
||||
const [ highlightedVisits, setHighlightedVisits ] = useState<NormalizedVisit[]>([]);
|
||||
const [ highlightedLabel, setHighlightedLabel ] = useState<string | undefined>();
|
||||
|
|
|
@ -18,19 +18,19 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
|||
|
||||
bottle.serviceFactory('ShortUrlVisits', () => ShortUrlVisits);
|
||||
bottle.decorator('ShortUrlVisits', connect(
|
||||
[ 'shortUrlVisits', 'shortUrlDetail', 'mercureInfo' ],
|
||||
[ 'shortUrlVisits', 'shortUrlDetail', 'mercureInfo', 'settings' ],
|
||||
[ 'getShortUrlVisits', 'getShortUrlDetail', 'cancelGetShortUrlVisits', 'createNewVisits', 'loadMercureInfo' ],
|
||||
));
|
||||
|
||||
bottle.serviceFactory('TagVisits', TagVisits, 'ColorGenerator');
|
||||
bottle.decorator('TagVisits', connect(
|
||||
[ 'tagVisits', 'mercureInfo' ],
|
||||
[ 'tagVisits', 'mercureInfo', 'settings' ],
|
||||
[ 'getTagVisits', 'cancelGetTagVisits', 'createNewVisits', 'loadMercureInfo' ],
|
||||
));
|
||||
|
||||
bottle.serviceFactory('OrphanVisits', () => OrphanVisits);
|
||||
bottle.decorator('OrphanVisits', connect(
|
||||
[ 'orphanVisits', 'mercureInfo' ],
|
||||
[ 'orphanVisits', 'mercureInfo', 'settings' ],
|
||||
[ 'getOrphanVisits', 'cancelGetOrphanVisits', 'createNewVisits', 'loadMercureInfo' ],
|
||||
));
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ describe('settingsReducer', () => {
|
|||
const realTimeUpdates = { enabled: true };
|
||||
const shortUrlCreation = { validateUrls: false };
|
||||
const ui = { theme: 'light' };
|
||||
const settings = { realTimeUpdates, shortUrlCreation, ui };
|
||||
const visits = { defaultInterval: 'last30Days' };
|
||||
const settings = { realTimeUpdates, shortUrlCreation, ui, visits };
|
||||
|
||||
describe('reducer', () => {
|
||||
it('returns realTimeUpdates when action is SET_SETTINGS', () => {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { Visit, VisitsInfo } from '../../src/visits/types';
|
|||
import LineChartCard from '../../src/visits/helpers/LineChartCard';
|
||||
import VisitsTable from '../../src/visits/VisitsTable';
|
||||
import { Result } from '../../src/utils/Result';
|
||||
import { Settings } from '../../src/settings/reducers/settings';
|
||||
|
||||
describe('<VisitStats />', () => {
|
||||
const visits = [ Mock.all<Visit>(), Mock.all<Visit>(), Mock.all<Visit>() ];
|
||||
|
@ -23,6 +24,7 @@ describe('<VisitStats />', () => {
|
|||
visitsInfo={Mock.of<VisitsInfo>(visitsInfo)}
|
||||
cancelGetVisits={() => {}}
|
||||
baseUrl={''}
|
||||
settings={Mock.all<Settings>()}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue