mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Refactored functionality for all time date range.
This commit is contained in:
parent
c3973f5fb5
commit
d06808985b
@ -10,11 +10,7 @@ export function RefreshButton({ websiteId, isLoading }) {
|
|||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
if (!isLoading && dateRange) {
|
if (!isLoading && dateRange) {
|
||||||
if (/^\d+/.test(dateRange.value)) {
|
setWebsiteDateRange(websiteId, dateRange);
|
||||||
setWebsiteDateRange(websiteId, dateRange.value);
|
|
||||||
} else {
|
|
||||||
setWebsiteDateRange(websiteId, dateRange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import PageviewsChart from 'components/metrics/PageviewsChart';
|
import PageviewsChart from 'components/metrics/PageviewsChart';
|
||||||
import { useApi, useDateRange, useTimezone, usePageQuery } from 'hooks';
|
import { useApi, useDateRange, useTimezone, usePageQuery } from 'hooks';
|
||||||
import { getDateArray, getDateLength } from 'lib/date';
|
import { getDateArray } from 'lib/date';
|
||||||
|
|
||||||
export function WebsiteChart({ websiteId }) {
|
export function WebsiteChart({ websiteId }) {
|
||||||
const [dateRange] = useDateRange(websiteId);
|
const [dateRange] = useDateRange(websiteId);
|
||||||
@ -43,7 +43,7 @@ export function WebsiteChart({ websiteId }) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return { pageviews: [], sessions: [] };
|
return { pageviews: [], sessions: [] };
|
||||||
}, [data, startDate, endDate, unit, modified]);
|
}, [data, startDate, endDate, unit]);
|
||||||
|
|
||||||
return <PageviewsChart websiteId={websiteId} data={chartData} unit={unit} loading={isLoading} />;
|
return <PageviewsChart websiteId={websiteId} data={chartData} unit={unit} loading={isLoading} />;
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ export function WebsiteHeader({ websiteId, showLinks = true, children }) {
|
|||||||
<Column className={styles.title} variant="two">
|
<Column className={styles.title} variant="two">
|
||||||
<Favicon domain={domain} />
|
<Favicon domain={domain} />
|
||||||
<Text>{name}</Text>
|
<Text>{name}</Text>
|
||||||
|
<ActiveUsers websiteId={websiteId} />
|
||||||
</Column>
|
</Column>
|
||||||
<Column className={styles.actions} variant="two">
|
<Column className={styles.actions} variant="two">
|
||||||
<ActiveUsers websiteId={websiteId} />
|
|
||||||
{showLinks && (
|
{showLinks && (
|
||||||
<Flexbox alignItems="center">
|
<Flexbox alignItems="center">
|
||||||
{links.map(({ label, icon, path }) => {
|
{links.map(({ label, icon, path }) => {
|
||||||
|
@ -1,20 +1,43 @@
|
|||||||
import { parseDateRange } from 'lib/date';
|
import { getMinimumUnit, parseDateRange } from 'lib/date';
|
||||||
import { setItem } from 'next-basics';
|
import { setItem } from 'next-basics';
|
||||||
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
|
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
|
||||||
import useLocale from './useLocale';
|
import useLocale from './useLocale';
|
||||||
import websiteStore, { setWebsiteDateRange } from 'store/websites';
|
import websiteStore, { setWebsiteDateRange } from 'store/websites';
|
||||||
import appStore, { setDateRange } from 'store/app';
|
import appStore, { setDateRange } from 'store/app';
|
||||||
|
import useApi from './useApi';
|
||||||
|
|
||||||
export function useDateRange(websiteId) {
|
export function useDateRange(websiteId) {
|
||||||
|
const { get } = useApi();
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
const websiteConfig = websiteStore(state => state[websiteId]?.dateRange);
|
const websiteConfig = websiteStore(state => state[websiteId]?.dateRange);
|
||||||
const defaultConfig = DEFAULT_DATE_RANGE;
|
const defaultConfig = DEFAULT_DATE_RANGE;
|
||||||
const globalConfig = appStore(state => state.dateRange);
|
const globalConfig = appStore(state => state.dateRange);
|
||||||
const dateRange = parseDateRange(websiteConfig || globalConfig || defaultConfig, locale);
|
const dateRange = parseDateRange(websiteConfig || globalConfig || defaultConfig, locale);
|
||||||
|
|
||||||
const saveDateRange = value => {
|
const saveDateRange = async value => {
|
||||||
if (websiteId) {
|
if (websiteId) {
|
||||||
setWebsiteDateRange(websiteId, value);
|
let dateRange = value;
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (value === 'all') {
|
||||||
|
const result = await get(`/websites/${websiteId}/daterange`);
|
||||||
|
const { mindate, maxdate } = result;
|
||||||
|
|
||||||
|
const startDate = new Date(mindate);
|
||||||
|
const endDate = new Date(maxdate);
|
||||||
|
|
||||||
|
dateRange = {
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
unit: getMinimumUnit(startDate, endDate),
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
dateRange = parseDateRange(value, locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setWebsiteDateRange(websiteId, dateRange);
|
||||||
} else {
|
} else {
|
||||||
setItem(DATE_RANGE_CONFIG, value);
|
setItem(DATE_RANGE_CONFIG, value);
|
||||||
setDateRange(value);
|
setDateRange(value);
|
||||||
|
@ -122,7 +122,7 @@ async function findUnique(data) {
|
|||||||
throw `${data.length} records found when expecting 1.`;
|
throw `${data.length} records found when expecting 1.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data[0] ?? null;
|
return findFirst(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findFirst(data) {
|
async function findFirst(data) {
|
||||||
|
11
lib/query.ts
11
lib/query.ts
@ -7,12 +7,15 @@ export async function parseDateRangeQuery(req: NextApiRequest) {
|
|||||||
|
|
||||||
// All-time
|
// All-time
|
||||||
if (+startAt === 0 && +endAt === 1) {
|
if (+startAt === 0 && +endAt === 1) {
|
||||||
const { min, max } = await getWebsiteDateRange(websiteId as string);
|
const result = await getWebsiteDateRange(websiteId as string);
|
||||||
|
const { min, max } = result[0];
|
||||||
|
const startDate = new Date(min);
|
||||||
|
const endDate = new Date(max);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startDate: min,
|
startDate,
|
||||||
endDate: max,
|
endDate,
|
||||||
unit: getMinimumUnit(min, max),
|
unit: getMinimumUnit(startDate, endDate),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,3 +137,10 @@ export interface RealtimeUpdate {
|
|||||||
events: any[];
|
events: any[];
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DateRange {
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
unit: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
32
pages/api/websites/[id]/daterange.ts
Normal file
32
pages/api/websites/[id]/daterange.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { WebsiteActive, NextApiRequestQueryBody } from 'lib/types';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { useAuth, useCors } from 'lib/middleware';
|
||||||
|
import { NextApiResponse } from 'next';
|
||||||
|
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||||
|
import { getWebsiteDateRange } from 'queries';
|
||||||
|
|
||||||
|
export interface WebsiteDateRangeRequestQuery {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async (
|
||||||
|
req: NextApiRequestQueryBody<WebsiteDateRangeRequestQuery>,
|
||||||
|
res: NextApiResponse<WebsiteActive>,
|
||||||
|
) => {
|
||||||
|
await useCors(req, res);
|
||||||
|
await useAuth(req, res);
|
||||||
|
|
||||||
|
const { id: websiteId } = req.query;
|
||||||
|
|
||||||
|
if (req.method === 'GET') {
|
||||||
|
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||||
|
return unauthorized(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await getWebsiteDateRange(websiteId);
|
||||||
|
|
||||||
|
return ok(res, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodNotAllowed(res);
|
||||||
|
};
|
@ -73,6 +73,7 @@ export default async (
|
|||||||
city,
|
city,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const prevPeriod = await getWebsiteStats(websiteId, {
|
const prevPeriod = await getWebsiteStats(websiteId, {
|
||||||
startDate: prevStartDate,
|
startDate: prevStartDate,
|
||||||
endDate: prevEndDate,
|
endDate: prevEndDate,
|
||||||
|
@ -16,32 +16,36 @@ async function relationalQuery(websiteId: string) {
|
|||||||
const { rawQuery } = prisma;
|
const { rawQuery } = prisma;
|
||||||
const website = await loadWebsite(websiteId);
|
const website = await loadWebsite(websiteId);
|
||||||
|
|
||||||
return rawQuery(
|
const result = await rawQuery(
|
||||||
`
|
`
|
||||||
select
|
select
|
||||||
min(created_at) as min,
|
min(created_at) as mindate,
|
||||||
max(created_at) as max
|
max(created_at) as maxdate
|
||||||
from website_event
|
from website_event
|
||||||
where website_id = {{websiteId::uuid}}
|
where website_id = {{websiteId::uuid}}
|
||||||
and created_at >= {{startDate}}
|
and created_at >= {{startDate}}
|
||||||
`,
|
`,
|
||||||
{ websiteId, startDate: maxDate(new Date(DEFAULT_RESET_DATE), new Date(website.resetAt)) },
|
{ websiteId, startDate: maxDate(new Date(DEFAULT_RESET_DATE), new Date(website.resetAt)) },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return result[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clickhouseQuery(websiteId: string) {
|
async function clickhouseQuery(websiteId: string) {
|
||||||
const { rawQuery } = clickhouse;
|
const { rawQuery } = clickhouse;
|
||||||
const website = await loadWebsite(websiteId);
|
const website = await loadWebsite(websiteId);
|
||||||
|
|
||||||
return rawQuery(
|
const result = await rawQuery(
|
||||||
`
|
`
|
||||||
select
|
select
|
||||||
min(created_at) as min,
|
min(created_at) as mindate,
|
||||||
max(created_at) as max
|
max(created_at) as maxdate
|
||||||
from website_event
|
from website_event
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
and created_at >= {startDate:DateTime}
|
and created_at >= {startDate:DateTime}
|
||||||
`,
|
`,
|
||||||
{ websiteId, startDate: maxDate(new Date(DEFAULT_RESET_DATE), new Date(website.resetAt)) },
|
{ websiteId, startDate: maxDate(new Date(DEFAULT_RESET_DATE), new Date(website.resetAt)) },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return result[0] ?? null;
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,20 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import app from './app';
|
import { DateRange } from 'lib/types';
|
||||||
import { parseDateRange } from 'lib/date';
|
|
||||||
|
|
||||||
const store = create(() => ({}));
|
const store = create(() => ({}));
|
||||||
|
|
||||||
export function getWebsiteDateRange(websiteId) {
|
export function getWebsiteDateRange(websiteId: string) {
|
||||||
return store.getState()?.[websiteId];
|
return store.getState()?.[websiteId];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWebsiteDateRange(websiteId, value) {
|
export function setWebsiteDateRange(websiteId: string, dateRange: DateRange) {
|
||||||
store.setState(
|
store.setState(
|
||||||
produce(state => {
|
produce(state => {
|
||||||
if (!state[websiteId]) {
|
if (!state[websiteId]) {
|
||||||
state[websiteId] = {};
|
state[websiteId] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
let dateRange = value;
|
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
const { locale } = app.getState();
|
|
||||||
dateRange = parseDateRange(value, locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
state[websiteId].dateRange = { ...dateRange, modified: Date.now() };
|
state[websiteId].dateRange = { ...dateRange, modified: Date.now() };
|
||||||
|
|
||||||
return state;
|
return state;
|
Loading…
x
Reference in New Issue
Block a user