mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Progress check-in for date compare.
This commit is contained in:
parent
24af06f3aa
commit
8cf7985dac
@ -7,11 +7,11 @@ import styles from './DateRangeSetting.module.css';
|
||||
|
||||
export function DateRangeSetting() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [dateRange, setDateRange] = useDateRange();
|
||||
const { dateRange, saveDateRange } = useDateRange();
|
||||
const { value } = dateRange;
|
||||
|
||||
const handleChange = (value: string | DateRange) => setDateRange(value);
|
||||
const handleReset = () => setDateRange(DEFAULT_DATE_RANGE);
|
||||
const handleChange = (value: string | DateRange) => saveDateRange(value);
|
||||
const handleReset = () => saveDateRange(DEFAULT_DATE_RANGE);
|
||||
|
||||
return (
|
||||
<Flexbox gap={10} width={300}>
|
||||
|
@ -4,17 +4,33 @@ import { getDateArray } from 'lib/date';
|
||||
import useWebsitePageviews from 'components/hooks/queries/useWebsitePageviews';
|
||||
import { useDateRange } from 'components/hooks';
|
||||
|
||||
export function WebsiteChart({ websiteId }: { websiteId: string }) {
|
||||
const [dateRange] = useDateRange(websiteId);
|
||||
export function WebsiteChart({
|
||||
websiteId,
|
||||
compareMode = false,
|
||||
}: {
|
||||
websiteId: string;
|
||||
compareMode: boolean;
|
||||
}) {
|
||||
const { dateRange, dateCompare } = useDateRange(websiteId);
|
||||
const { startDate, endDate, unit } = dateRange;
|
||||
const { data, isLoading } = useWebsitePageviews(websiteId);
|
||||
const { data, isLoading } = useWebsitePageviews(websiteId, compareMode ? dateCompare : undefined);
|
||||
const { pageviews, sessions, compare } = (data || {}) as any;
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (data) {
|
||||
return {
|
||||
pageviews: getDateArray(data.pageviews, startDate, endDate, unit),
|
||||
sessions: getDateArray(data.sessions, startDate, endDate, unit),
|
||||
const result = {
|
||||
pageviews: getDateArray(pageviews, startDate, endDate, unit),
|
||||
sessions: getDateArray(sessions, startDate, endDate, unit),
|
||||
};
|
||||
|
||||
if (compare) {
|
||||
result['compare'] = {
|
||||
pageviews: result.pageviews.map(({ x }, i) => ({ x, y: compare.pageviews[i].y })),
|
||||
sessions: result.sessions.map(({ x }, i) => ({ x, y: compare.sessions[i].y })),
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return { pageviews: [], sessions: [] };
|
||||
}, [data, startDate, endDate, unit]);
|
||||
|
@ -21,7 +21,9 @@ export function WebsiteFilterButton({
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { renderUrl, router } = useNavigation();
|
||||
const { fields } = useFields();
|
||||
const [{ startDate, endDate }] = useDateRange(websiteId);
|
||||
const {
|
||||
dateRange: { startDate, endDate },
|
||||
} = useDateRange(websiteId);
|
||||
|
||||
const handleAddFilter = ({ name, operator, value }) => {
|
||||
const prefix = OPERATOR_PREFIXES[operator];
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useMessages, useSticky } from 'components/hooks';
|
||||
import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
|
||||
@ -9,6 +8,7 @@ import WebsiteFilterButton from './WebsiteFilterButton';
|
||||
import useWebsiteStats from 'components/hooks/queries/useWebsiteStats';
|
||||
import styles from './WebsiteMetricsBar.module.css';
|
||||
import { Dropdown, Item } from 'react-basics';
|
||||
import useStore, { setWebsiteDateCompare } from 'store/websites';
|
||||
|
||||
export function WebsiteMetricsBar({
|
||||
websiteId,
|
||||
@ -22,9 +22,12 @@ export function WebsiteMetricsBar({
|
||||
compareMode?: boolean;
|
||||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [compare, setCompare] = useState('prev');
|
||||
const dateCompare = useStore(state => state[websiteId]?.dateCompare);
|
||||
const { ref, isSticky } = useSticky({ enabled: sticky });
|
||||
const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId, compare);
|
||||
const { data, isLoading, isFetched, error } = useWebsiteStats(
|
||||
websiteId,
|
||||
compareMode && dateCompare,
|
||||
);
|
||||
|
||||
const { pageviews, visitors, visits, bounces, totaltime } = data || {};
|
||||
|
||||
@ -106,10 +109,10 @@ export function WebsiteMetricsBar({
|
||||
<Dropdown
|
||||
className={styles.dropdown}
|
||||
items={items}
|
||||
value={compare}
|
||||
value={dateCompare || 'prev'}
|
||||
renderValue={value => items.find(i => i.value === value)?.label}
|
||||
alignment="end"
|
||||
onChange={(e: any) => setCompare(e)}
|
||||
onChange={(value: any) => setWebsiteDateCompare(websiteId, value)}
|
||||
>
|
||||
{items.map(({ label, value }) => (
|
||||
<Item key={value}>{label}</Item>
|
||||
|
@ -21,7 +21,7 @@ export function WebsiteComparePage({ websiteId }) {
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<FilterTags websiteId={websiteId} params={params} />
|
||||
<WebsiteMetricsBar websiteId={websiteId} compareMode={true} />
|
||||
<WebsiteChart websiteId={websiteId} />
|
||||
<WebsiteChart websiteId={websiteId} compareMode={true} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import styles from './EventDataMetricsBar.module.css';
|
||||
export function EventDataMetricsBar({ websiteId }: { websiteId: string }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { get, useQuery } = useApi();
|
||||
const [dateRange] = useDateRange(websiteId);
|
||||
const { dateRange } = useDateRange(websiteId);
|
||||
const { startDate, endDate } = dateRange;
|
||||
|
||||
const { data, error, isLoading, isFetched } = useQuery({
|
||||
|
@ -6,7 +6,7 @@ import { useDateRange, useApi, useNavigation } from 'components/hooks';
|
||||
import styles from './WebsiteEventData.module.css';
|
||||
|
||||
function useData(websiteId: string, event: string) {
|
||||
const [dateRange] = useDateRange(websiteId);
|
||||
const { dateRange } = useDateRange(websiteId);
|
||||
const { startDate, endDate } = dateRange;
|
||||
const { get, useQuery } = useApi();
|
||||
const { data, error, isLoading } = useQuery({
|
||||
|
@ -26,7 +26,7 @@ export function BarChart(props: BarChartProps) {
|
||||
stacked = false,
|
||||
} = props;
|
||||
|
||||
const options = useMemo(() => {
|
||||
const options: any = useMemo(() => {
|
||||
return {
|
||||
scales: {
|
||||
x: {
|
||||
|
@ -79,15 +79,19 @@ export function Chart({
|
||||
};
|
||||
|
||||
const updateChart = (data: any) => {
|
||||
chart.current.data.datasets.forEach((dataset: { data: any }, index: string | number) => {
|
||||
if (data?.datasets[index]) {
|
||||
dataset.data = data?.datasets[index]?.data;
|
||||
if (data.datasets.length === chart.current.data.datasets.length) {
|
||||
chart.current.data.datasets.forEach((dataset: { data: any }, index: string | number) => {
|
||||
if (data?.datasets[index]) {
|
||||
dataset.data = data?.datasets[index]?.data;
|
||||
|
||||
if (chart.current.legend.legendItems[index]) {
|
||||
chart.current.legend.legendItems[index].text = data?.datasets[index]?.label;
|
||||
if (chart.current.legend.legendItems[index]) {
|
||||
chart.current.legend.legendItems[index].text = data?.datasets[index]?.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
chart.current.data.datasets = data.datasets;
|
||||
}
|
||||
|
||||
chart.current.options = options;
|
||||
|
||||
|
@ -4,14 +4,15 @@ import { useFilterParams } from '..//useFilterParams';
|
||||
|
||||
export function useWebsitePageviews(
|
||||
websiteId: string,
|
||||
compare?: string,
|
||||
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
|
||||
) {
|
||||
const { get, useQuery } = useApi();
|
||||
const params = useFilterParams(websiteId);
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['websites:pageviews', { websiteId, ...params }],
|
||||
queryFn: () => get(`/websites/${websiteId}/pageviews`, params),
|
||||
queryKey: ['websites:pageviews', { websiteId, ...params, compare }],
|
||||
queryFn: () => get(`/websites/${websiteId}/pageviews`, { ...params, compare }),
|
||||
enabled: !!websiteId,
|
||||
...options,
|
||||
});
|
||||
|
@ -1,19 +1,25 @@
|
||||
import { getMinimumUnit, parseDateRange } from 'lib/date';
|
||||
import { setItem } from 'next-basics';
|
||||
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
|
||||
import websiteStore, { setWebsiteDateRange } from 'store/websites';
|
||||
import { DATE_RANGE_CONFIG, DEFAULT_DATE_COMPARE, DEFAULT_DATE_RANGE } from 'lib/constants';
|
||||
import websiteStore, { setWebsiteDateRange, setWebsiteDateCompare } from 'store/websites';
|
||||
import appStore, { setDateRange } from 'store/app';
|
||||
import { DateRange } from 'lib/types';
|
||||
import { useLocale } from './useLocale';
|
||||
import { useApi } from './queries/useApi';
|
||||
|
||||
export function useDateRange(websiteId?: string): [DateRange, (value: string | DateRange) => void] {
|
||||
export function useDateRange(websiteId?: string): {
|
||||
dateRange: DateRange;
|
||||
saveDateRange: (value: string | DateRange) => void;
|
||||
dateCompare: string;
|
||||
saveDateCompare: (value: string) => void;
|
||||
} {
|
||||
const { get } = useApi();
|
||||
const { locale } = useLocale();
|
||||
const websiteConfig = websiteStore(state => state[websiteId]?.dateRange);
|
||||
const defaultConfig = DEFAULT_DATE_RANGE;
|
||||
const globalConfig = appStore(state => state.dateRange);
|
||||
const dateRange = parseDateRange(websiteConfig || globalConfig || defaultConfig, locale);
|
||||
const dateCompare = websiteStore(state => state[websiteId]?.dateCompare || DEFAULT_DATE_COMPARE);
|
||||
|
||||
const saveDateRange = async (value: DateRange | string) => {
|
||||
if (websiteId) {
|
||||
@ -45,7 +51,11 @@ export function useDateRange(websiteId?: string): [DateRange, (value: string | D
|
||||
}
|
||||
};
|
||||
|
||||
return [dateRange, saveDateRange];
|
||||
const saveDateCompare = (value: string) => {
|
||||
setWebsiteDateCompare(websiteId, value);
|
||||
};
|
||||
|
||||
return { dateRange, saveDateRange, dateCompare, saveDateCompare };
|
||||
}
|
||||
|
||||
export default useDateRange;
|
||||
|
@ -4,7 +4,7 @@ import { useTimezone } from './useTimezone';
|
||||
import { zonedTimeToUtc } from 'date-fns-tz';
|
||||
|
||||
export function useFilterParams(websiteId: string) {
|
||||
const [dateRange] = useDateRange(websiteId);
|
||||
const { dateRange } = useDateRange(websiteId);
|
||||
const { startDate, endDate, unit } = dateRange;
|
||||
const { timezone } = useTimezone();
|
||||
const {
|
||||
|
@ -12,7 +12,7 @@ export function RefreshButton({
|
||||
isLoading?: boolean;
|
||||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const [dateRange] = useDateRange(websiteId);
|
||||
const { dateRange } = useDateRange(websiteId);
|
||||
|
||||
function handleClick() {
|
||||
if (!isLoading && dateRange) {
|
||||
|
@ -8,17 +8,17 @@ import { DateRange } from 'lib/types';
|
||||
|
||||
export function WebsiteDateFilter({ websiteId }: { websiteId: string }) {
|
||||
const { dir } = useLocale();
|
||||
const [dateRange, setDateRange] = useDateRange(websiteId);
|
||||
const { dateRange, saveDateRange } = useDateRange(websiteId);
|
||||
const { value, startDate, endDate, offset } = dateRange;
|
||||
const disableForward =
|
||||
value === 'all' || isAfter(getOffsetDateRange(dateRange, 1).startDate, new Date());
|
||||
|
||||
const handleChange = (value: string | DateRange) => {
|
||||
setDateRange(value);
|
||||
saveDateRange(value);
|
||||
};
|
||||
|
||||
const handleIncrement = (increment: number) => {
|
||||
setDateRange(getOffsetDateRange(dateRange, increment));
|
||||
saveDateRange(getOffsetDateRange(dateRange, increment));
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -13,7 +13,9 @@ export interface EventsChartProps {
|
||||
}
|
||||
|
||||
export function EventsChart({ websiteId, className }: EventsChartProps) {
|
||||
const [{ startDate, endDate, unit }] = useDateRange(websiteId);
|
||||
const {
|
||||
dateRange: { startDate, endDate, unit },
|
||||
} = useDateRange(websiteId);
|
||||
const { locale } = useLocale();
|
||||
const { data, isLoading } = useWebsiteEvents(websiteId);
|
||||
|
||||
|
@ -24,7 +24,7 @@ export function FilterTags({
|
||||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { formatValue } = useFormat();
|
||||
const [dateRange] = useDateRange(websiteId);
|
||||
const { dateRange } = useDateRange(websiteId);
|
||||
const {
|
||||
router,
|
||||
renderUrl,
|
||||
|
@ -5,13 +5,9 @@
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.card.compare {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.card.compare .change {
|
||||
font-size: 16px;
|
||||
padding: 5px 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.card:first-child {
|
||||
@ -23,7 +19,7 @@
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 40px;
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
color: var(--base900);
|
||||
@ -46,7 +42,7 @@
|
||||
gap: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 0 5px;
|
||||
padding: 0.1em 0.5em;
|
||||
border-radius: 5px;
|
||||
color: var(--base500);
|
||||
align-self: flex-start;
|
||||
|
@ -48,7 +48,7 @@ export const MetricCard = ({
|
||||
[styles.hide]: ~~change === 0,
|
||||
})}
|
||||
>
|
||||
<Icon rotate={positive ? -45 : 45} size={showPrevious ? 'sm' : 'xs'}>
|
||||
<Icon rotate={positive || reverseColors ? -45 : 45} size={showPrevious ? 'md' : 'xs'}>
|
||||
<Icons.ArrowRight />
|
||||
</Icon>
|
||||
<animated.span title={changeProps?.x as any}>
|
||||
|
@ -5,8 +5,12 @@ import { renderDateLabels } from 'lib/charts';
|
||||
|
||||
export interface PageviewsChartProps extends BarChartProps {
|
||||
data: {
|
||||
sessions: any[];
|
||||
pageviews: any[];
|
||||
sessions: any[];
|
||||
compare?: {
|
||||
pageviews: any[];
|
||||
sessions: any[];
|
||||
};
|
||||
};
|
||||
unit: string;
|
||||
isLoading?: boolean;
|
||||
@ -36,7 +40,25 @@ export function PageviewsChart({ data, unit, isLoading, ...props }: PageviewsCha
|
||||
borderWidth: 1,
|
||||
...colors.chart.views,
|
||||
},
|
||||
],
|
||||
data.compare
|
||||
? {
|
||||
type: 'line',
|
||||
label: formatMessage(labels.visitors),
|
||||
data: data.compare.pageviews,
|
||||
borderWidth: 2,
|
||||
borderColor: '#f15bb5',
|
||||
}
|
||||
: null,
|
||||
data.compare
|
||||
? {
|
||||
type: 'line',
|
||||
label: formatMessage(labels.visits),
|
||||
data: data.compare.sessions,
|
||||
borderWidth: 2,
|
||||
borderColor: '#9b5de5',
|
||||
}
|
||||
: null,
|
||||
].filter(n => n),
|
||||
};
|
||||
}, [data, locale]);
|
||||
|
||||
|
@ -20,6 +20,7 @@ export const DEFAULT_DATE_RANGE = '24hour';
|
||||
export const DEFAULT_WEBSITE_LIMIT = 10;
|
||||
export const DEFAULT_RESET_DATE = '2000-01-01';
|
||||
export const DEFAULT_PAGE_SIZE = 10;
|
||||
export const DEFAULT_DATE_COMPARE = 'prev';
|
||||
|
||||
export const REALTIME_RANGE = 30;
|
||||
export const REALTIME_INTERVAL = 5000;
|
||||
|
@ -326,3 +326,13 @@ export function getDateLength(startDate: Date, endDate: Date, unit: string | num
|
||||
const { diff } = DATE_FUNCTIONS[unit];
|
||||
return diff(endDate, startDate) + 1;
|
||||
}
|
||||
|
||||
export function getCompareDate(compare: string, startDate: Date, endDate: Date) {
|
||||
if (compare === 'yoy') {
|
||||
return { startDate: subYears(startDate, 1), endDate: subYears(endDate, 1) };
|
||||
}
|
||||
|
||||
const diff = differenceInMinutes(endDate, startDate);
|
||||
|
||||
return { startDate: subMinutes(startDate, diff), endDate: subMinutes(endDate, diff) };
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as yup from 'yup';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { getRequestFilters, getRequestDateRange } from 'lib/request';
|
||||
@ -5,6 +6,8 @@ import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { getPageviewStats, getSessionStats } from 'queries';
|
||||
import { TimezoneTest, UnitTypeTest } from 'lib/yup';
|
||||
import { getCompareDate } from 'lib/date';
|
||||
|
||||
export interface WebsitePageviewRequestQuery {
|
||||
websiteId: string;
|
||||
@ -21,10 +24,9 @@ export interface WebsitePageviewRequestQuery {
|
||||
country?: string;
|
||||
region: string;
|
||||
city?: string;
|
||||
compare?: string;
|
||||
}
|
||||
|
||||
import { TimezoneTest, UnitTypeTest } from 'lib/yup';
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
@ -41,6 +43,7 @@ const schema = {
|
||||
country: yup.string(),
|
||||
region: yup.string(),
|
||||
city: yup.string(),
|
||||
compare: yup.string(),
|
||||
}),
|
||||
};
|
||||
|
||||
@ -52,7 +55,7 @@ export default async (
|
||||
await useAuth(req, res);
|
||||
await useValidate(schema, req, res);
|
||||
|
||||
const { websiteId, timezone } = req.query;
|
||||
const { websiteId, timezone, compare } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
@ -74,6 +77,40 @@ export default async (
|
||||
getSessionStats(websiteId, filters),
|
||||
]);
|
||||
|
||||
if (compare) {
|
||||
const { startDate: compareStartDate, endDate: compareEndDate } = getCompareDate(
|
||||
compare,
|
||||
startDate,
|
||||
endDate,
|
||||
);
|
||||
|
||||
const [comparePageviews, compareSessions] = await Promise.all([
|
||||
getPageviewStats(websiteId, {
|
||||
...filters,
|
||||
startDate: compareStartDate,
|
||||
endDate: compareEndDate,
|
||||
}),
|
||||
getSessionStats(websiteId, {
|
||||
...filters,
|
||||
startDate: compareStartDate,
|
||||
endDate: compareEndDate,
|
||||
}),
|
||||
]);
|
||||
|
||||
return ok(res, {
|
||||
pageviews,
|
||||
sessions,
|
||||
startDate,
|
||||
endDate,
|
||||
compare: {
|
||||
pageviews: comparePageviews,
|
||||
sessions: compareSessions,
|
||||
startDate: compareStartDate,
|
||||
endDate: compareEndDate,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return ok(res, { pageviews, sessions });
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { subMinutes, differenceInMinutes } from 'date-fns';
|
||||
import * as yup from 'yup';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
@ -6,6 +6,7 @@ import { useAuth, useCors, useValidate } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody, WebsiteStats } from 'lib/types';
|
||||
import { getRequestFilters, getRequestDateRange } from 'lib/request';
|
||||
import { getWebsiteStats } from 'queries';
|
||||
import { getCompareDate } from 'lib/date';
|
||||
|
||||
export interface WebsiteStatsRequestQuery {
|
||||
websiteId: string;
|
||||
@ -25,7 +26,6 @@ export interface WebsiteStatsRequestQuery {
|
||||
compare?: string;
|
||||
}
|
||||
|
||||
import * as yup from 'yup';
|
||||
const schema = {
|
||||
GET: yup.object().shape({
|
||||
websiteId: yup.string().uuid().required(),
|
||||
@ -54,7 +54,7 @@ export default async (
|
||||
await useAuth(req, res);
|
||||
await useValidate(schema, req, res);
|
||||
|
||||
const { websiteId } = req.query;
|
||||
const { websiteId, compare } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
@ -62,9 +62,11 @@ export default async (
|
||||
}
|
||||
|
||||
const { startDate, endDate } = await getRequestDateRange(req);
|
||||
const diff = differenceInMinutes(endDate, startDate);
|
||||
const prevStartDate = subMinutes(startDate, diff);
|
||||
const prevEndDate = subMinutes(endDate, diff);
|
||||
const { startDate: compareStartDate, endDate: compareEndDate } = getCompareDate(
|
||||
compare,
|
||||
startDate,
|
||||
endDate,
|
||||
);
|
||||
|
||||
const filters = getRequestFilters(req);
|
||||
|
||||
@ -72,8 +74,8 @@ export default async (
|
||||
|
||||
const prevPeriod = await getWebsiteStats(websiteId, {
|
||||
...filters,
|
||||
startDate: prevStartDate,
|
||||
endDate: prevEndDate,
|
||||
startDate: compareStartDate,
|
||||
endDate: compareEndDate,
|
||||
});
|
||||
|
||||
const stats = Object.keys(metrics[0]).reduce((obj, key) => {
|
||||
|
@ -67,7 +67,7 @@ async function clickhouseQuery(
|
||||
`,
|
||||
params,
|
||||
).then(result => {
|
||||
return Object.values(result).map(a => {
|
||||
return Object.values(result).map((a: any) => {
|
||||
return { x: a.x, y: Number(a.y) };
|
||||
});
|
||||
});
|
||||
|
@ -18,4 +18,18 @@ export function setWebsiteDateRange(websiteId: string, dateRange: DateRange) {
|
||||
);
|
||||
}
|
||||
|
||||
export function setWebsiteDateCompare(websiteId: string, dateCompare: string) {
|
||||
store.setState(
|
||||
produce(state => {
|
||||
if (!state[websiteId]) {
|
||||
state[websiteId] = {};
|
||||
}
|
||||
|
||||
state[websiteId].dateCompare = dateCompare;
|
||||
|
||||
return state;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export default store;
|
||||
|
Loading…
Reference in New Issue
Block a user