Refactored functionality for all time date range.

This commit is contained in:
Mike Cao 2023-07-26 13:42:55 -07:00
parent c3973f5fb5
commit d06808985b
11 changed files with 91 additions and 33 deletions

View File

@ -10,13 +10,9 @@ export function RefreshButton({ websiteId, isLoading }) {
function handleClick() {
if (!isLoading && dateRange) {
if (/^\d+/.test(dateRange.value)) {
setWebsiteDateRange(websiteId, dateRange.value);
} else {
setWebsiteDateRange(websiteId, dateRange);
}
}
}
return (
<TooltipPopup label={formatMessage(labels.refresh)}>

View File

@ -1,7 +1,7 @@
import { useMemo } from 'react';
import PageviewsChart from 'components/metrics/PageviewsChart';
import { useApi, useDateRange, useTimezone, usePageQuery } from 'hooks';
import { getDateArray, getDateLength } from 'lib/date';
import { getDateArray } from 'lib/date';
export function WebsiteChart({ websiteId }) {
const [dateRange] = useDateRange(websiteId);
@ -43,7 +43,7 @@ export function WebsiteChart({ websiteId }) {
};
}
return { pageviews: [], sessions: [] };
}, [data, startDate, endDate, unit, modified]);
}, [data, startDate, endDate, unit]);
return <PageviewsChart websiteId={websiteId} data={chartData} unit={unit} loading={isLoading} />;
}

View File

@ -42,9 +42,9 @@ export function WebsiteHeader({ websiteId, showLinks = true, children }) {
<Column className={styles.title} variant="two">
<Favicon domain={domain} />
<Text>{name}</Text>
<ActiveUsers websiteId={websiteId} />
</Column>
<Column className={styles.actions} variant="two">
<ActiveUsers websiteId={websiteId} />
{showLinks && (
<Flexbox alignItems="center">
{links.map(({ label, icon, path }) => {

View File

@ -1,20 +1,43 @@
import { parseDateRange } from 'lib/date';
import { getMinimumUnit, parseDateRange } from 'lib/date';
import { setItem } from 'next-basics';
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
import useLocale from './useLocale';
import websiteStore, { setWebsiteDateRange } from 'store/websites';
import appStore, { setDateRange } from 'store/app';
import useApi from './useApi';
export function useDateRange(websiteId) {
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 saveDateRange = value => {
const saveDateRange = async value => {
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 {
setItem(DATE_RANGE_CONFIG, value);
setDateRange(value);

View File

@ -122,7 +122,7 @@ async function findUnique(data) {
throw `${data.length} records found when expecting 1.`;
}
return data[0] ?? null;
return findFirst(data);
}
async function findFirst(data) {

View File

@ -7,12 +7,15 @@ export async function parseDateRangeQuery(req: NextApiRequest) {
// All-time
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 {
startDate: min,
endDate: max,
unit: getMinimumUnit(min, max),
startDate,
endDate,
unit: getMinimumUnit(startDate, endDate),
};
}

View File

@ -137,3 +137,10 @@ export interface RealtimeUpdate {
events: any[];
timestamp: number;
}
export interface DateRange {
startDate: Date;
endDate: Date;
unit: string;
value: string;
}

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

View File

@ -73,6 +73,7 @@ export default async (
city,
},
});
const prevPeriod = await getWebsiteStats(websiteId, {
startDate: prevStartDate,
endDate: prevEndDate,

View File

@ -16,32 +16,36 @@ async function relationalQuery(websiteId: string) {
const { rawQuery } = prisma;
const website = await loadWebsite(websiteId);
return rawQuery(
const result = await rawQuery(
`
select
min(created_at) as min,
max(created_at) as max
min(created_at) as mindate,
max(created_at) as maxdate
from website_event
where website_id = {{websiteId::uuid}}
and created_at >= {{startDate}}
`,
{ websiteId, startDate: maxDate(new Date(DEFAULT_RESET_DATE), new Date(website.resetAt)) },
);
return result[0] ?? null;
}
async function clickhouseQuery(websiteId: string) {
const { rawQuery } = clickhouse;
const website = await loadWebsite(websiteId);
return rawQuery(
const result = await rawQuery(
`
select
min(created_at) as min,
max(created_at) as max
min(created_at) as mindate,
max(created_at) as maxdate
from website_event
where website_id = {websiteId:UUID}
and created_at >= {startDate:DateTime}
`,
{ websiteId, startDate: maxDate(new Date(DEFAULT_RESET_DATE), new Date(website.resetAt)) },
);
return result[0] ?? null;
}

View File

@ -1,28 +1,20 @@
import { create } from 'zustand';
import produce from 'immer';
import app from './app';
import { parseDateRange } from 'lib/date';
import { DateRange } from 'lib/types';
const store = create(() => ({}));
export function getWebsiteDateRange(websiteId) {
export function getWebsiteDateRange(websiteId: string) {
return store.getState()?.[websiteId];
}
export function setWebsiteDateRange(websiteId, value) {
export function setWebsiteDateRange(websiteId: string, dateRange: DateRange) {
store.setState(
produce(state => {
if (!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() };
return state;