diff --git a/components/input/RefreshButton.js b/components/input/RefreshButton.js
index 444f3247..de7ce8cf 100644
--- a/components/input/RefreshButton.js
+++ b/components/input/RefreshButton.js
@@ -10,11 +10,7 @@ export function RefreshButton({ websiteId, isLoading }) {
function handleClick() {
if (!isLoading && dateRange) {
- if (/^\d+/.test(dateRange.value)) {
- setWebsiteDateRange(websiteId, dateRange.value);
- } else {
- setWebsiteDateRange(websiteId, dateRange);
- }
+ setWebsiteDateRange(websiteId, dateRange);
}
}
diff --git a/components/pages/websites/WebsiteChart.js b/components/pages/websites/WebsiteChart.js
index a4d94bd1..fac0fb37 100644
--- a/components/pages/websites/WebsiteChart.js
+++ b/components/pages/websites/WebsiteChart.js
@@ -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 ;
}
diff --git a/components/pages/websites/WebsiteHeader.js b/components/pages/websites/WebsiteHeader.js
index 76f4e4f7..f7db6431 100644
--- a/components/pages/websites/WebsiteHeader.js
+++ b/components/pages/websites/WebsiteHeader.js
@@ -42,9 +42,9 @@ export function WebsiteHeader({ websiteId, showLinks = true, children }) {
{name}
+
-
{showLinks && (
{links.map(({ label, icon, path }) => {
diff --git a/hooks/useDateRange.js b/hooks/useDateRange.js
index 17552805..1e1b0616 100644
--- a/hooks/useDateRange.js
+++ b/hooks/useDateRange.js
@@ -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);
diff --git a/lib/clickhouse.ts b/lib/clickhouse.ts
index 10722fff..3684d075 100644
--- a/lib/clickhouse.ts
+++ b/lib/clickhouse.ts
@@ -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) {
diff --git a/lib/query.ts b/lib/query.ts
index 09b77df8..88ce62d4 100644
--- a/lib/query.ts
+++ b/lib/query.ts
@@ -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),
};
}
diff --git a/lib/types.ts b/lib/types.ts
index 2e1ed986..7c91ec4f 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -137,3 +137,10 @@ export interface RealtimeUpdate {
events: any[];
timestamp: number;
}
+
+export interface DateRange {
+ startDate: Date;
+ endDate: Date;
+ unit: string;
+ value: string;
+}
diff --git a/pages/api/websites/[id]/daterange.ts b/pages/api/websites/[id]/daterange.ts
new file mode 100644
index 00000000..dc043560
--- /dev/null
+++ b/pages/api/websites/[id]/daterange.ts
@@ -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,
+ res: NextApiResponse,
+) => {
+ 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);
+};
diff --git a/pages/api/websites/[id]/stats.ts b/pages/api/websites/[id]/stats.ts
index 34347fe5..3e2b96a8 100644
--- a/pages/api/websites/[id]/stats.ts
+++ b/pages/api/websites/[id]/stats.ts
@@ -73,6 +73,7 @@ export default async (
city,
},
});
+
const prevPeriod = await getWebsiteStats(websiteId, {
startDate: prevStartDate,
endDate: prevEndDate,
diff --git a/queries/analytics/stats/getWebsiteDateRange.ts b/queries/analytics/stats/getWebsiteDateRange.ts
index 1f94c398..45885e45 100644
--- a/queries/analytics/stats/getWebsiteDateRange.ts
+++ b/queries/analytics/stats/getWebsiteDateRange.ts
@@ -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;
}
diff --git a/store/websites.js b/store/websites.ts
similarity index 52%
rename from store/websites.js
rename to store/websites.ts
index 34f8242d..0d210af6 100644
--- a/store/websites.js
+++ b/store/websites.ts
@@ -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;