Merge branch 'dev' into search-formatted-metrics

This commit is contained in:
Mike Cao 2024-11-28 16:36:29 -08:00 committed by GitHub
commit 4ab8b1ff91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
807 changed files with 45367 additions and 8474 deletions

View file

@ -1,10 +1,18 @@
export * from './queries/useApi';
export * from './queries/useConfig';
export * from './queries/useFilterQuery';
export * from './queries/useEventDataEvents';
export * from './queries/useEventDataProperties';
export * from './queries/useEventDataValues';
export * from './queries/useLogin';
export * from './queries/useRealtime';
export * from './queries/useReport';
export * from './queries/useReports';
export * from './queries/useSessionActivity';
export * from './queries/useSessionData';
export * from './queries/useSessionDataProperties';
export * from './queries/useSessionDataValues';
export * from './queries/useWebsiteSession';
export * from './queries/useWebsiteSessions';
export * from './queries/useWebsiteSessionsWeekly';
export * from './queries/useShareToken';
export * from './queries/useTeam';
export * from './queries/useTeams';
@ -15,8 +23,10 @@ export * from './queries/useUsers';
export * from './queries/useWebsite';
export * from './queries/useWebsites';
export * from './queries/useWebsiteEvents';
export * from './queries/useWebsiteEventsSeries';
export * from './queries/useWebsiteMetrics';
export * from './queries/useWebsiteValues';
export * from './useApi';
export * from './useCountryNames';
export * from './useDateRange';
export * from './useDocumentClick';
@ -30,6 +40,8 @@ export * from './useLocale';
export * from './useMessages';
export * from './useModified';
export * from './useNavigation';
export * from './usePagedQuery';
export * from './useRegionNames';
export * from './useSticky';
export * from './useTeamUrl';
export * from './useTheme';

View file

@ -1,6 +1,6 @@
import { useEffect } from 'react';
import useStore, { setConfig } from 'store/app';
import { useApi } from './useApi';
import { useApi } from '../useApi';
let loading = false;

View file

@ -0,0 +1,20 @@
import { useApi } from '../useApi';
import { UseQueryOptions } from '@tanstack/react-query';
import { useFilterParams } from '../useFilterParams';
export function useEventDataEvents(
websiteId: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
return useQuery({
queryKey: ['websites:event-data:events', { websiteId, ...params }],
queryFn: () => get(`/websites/${websiteId}/event-data/events`, { ...params }),
enabled: !!websiteId,
...options,
});
}
export default useEventDataEvents;

View file

@ -0,0 +1,20 @@
import { UseQueryOptions } from '@tanstack/react-query';
import { useApi } from '../useApi';
import { useFilterParams } from '../useFilterParams';
export function useEventDataProperties(
websiteId: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
return useQuery<any>({
queryKey: ['websites:event-data:properties', { websiteId, ...params }],
queryFn: () => get(`/websites/${websiteId}/event-data/properties`, { ...params }),
enabled: !!websiteId,
...options,
});
}
export default useEventDataProperties;

View file

@ -0,0 +1,23 @@
import { UseQueryOptions } from '@tanstack/react-query';
import { useApi } from '../useApi';
import { useFilterParams } from '../useFilterParams';
export function useEventDataValues(
websiteId: string,
eventName: string,
propertyName: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
return useQuery<any>({
queryKey: ['websites:event-data:values', { websiteId, eventName, propertyName, ...params }],
queryFn: () =>
get(`/websites/${websiteId}/event-data/values`, { ...params, eventName, propertyName }),
enabled: !!(websiteId && propertyName),
...options,
});
}
export default useEventDataValues;

View file

@ -1,6 +1,6 @@
import useStore, { setUser } from 'store/app';
import useApi from './useApi';
import { UseQueryResult } from '@tanstack/react-query';
import useStore, { setUser } from 'store/app';
import { useApi } from '../useApi';
const selector = (state: { user: any }) => state.user;

View file

@ -1,87 +1,21 @@
import { useMemo, useRef } from 'react';
import { useTimezone } from 'components/hooks';
import { REALTIME_INTERVAL } from 'lib/constants';
import { RealtimeData } from 'lib/types';
import { useApi } from 'components/hooks';
import { REALTIME_INTERVAL, REALTIME_RANGE } from 'lib/constants';
import { startOfMinute, subMinutes } from 'date-fns';
import { percentFilter } from 'lib/filters';
import thenby from 'thenby';
function mergeData(state = [], data = [], time: number) {
const ids = state.map(({ id }) => id);
return state
.concat(data.filter(({ id }) => !ids.includes(id)))
.filter(({ timestamp }) => timestamp >= time);
}
import { useApi } from '../useApi';
export function useRealtime(websiteId: string) {
const currentData = useRef({
pageviews: [],
sessions: [],
events: [],
countries: [],
visitors: [],
timestamp: 0,
});
const { get, useQuery } = useApi();
const { timezone } = useTimezone();
const { data, isLoading, error } = useQuery<RealtimeData>({
queryKey: ['realtime', websiteId],
queryKey: ['realtime', { websiteId, timezone }],
queryFn: async () => {
const state = currentData.current;
const data = await get(`/realtime/${websiteId}`, { startAt: state?.timestamp || 0 });
const date = subMinutes(startOfMinute(new Date()), REALTIME_RANGE);
const time = date.getTime();
const { pageviews, sessions, events, timestamp } = data;
return {
pageviews: mergeData(state?.pageviews, pageviews, time),
sessions: mergeData(state?.sessions, sessions, time),
events: mergeData(state?.events, events, time),
timestamp,
};
return get(`/realtime/${websiteId}`, { timezone });
},
enabled: !!websiteId,
refetchInterval: REALTIME_INTERVAL,
});
const realtimeData: RealtimeData = useMemo(() => {
if (!data) {
return { pageviews: [], sessions: [], events: [], countries: [], visitors: [], timestamp: 0 };
}
data.countries = percentFilter(
data.sessions
.reduce((arr, data) => {
if (!arr.find(({ id }) => id === data.id)) {
return arr.concat(data);
}
return arr;
}, [])
.reduce((arr: { x: any; y: number }[], { country }: any) => {
if (country) {
const row = arr.find(({ x }) => x === country);
if (!row) {
arr.push({ x: country, y: 1 });
} else {
row.y += 1;
}
}
return arr;
}, [])
.sort(thenby.firstBy('y', -1)),
);
data.visitors = data.sessions.reduce((arr, val) => {
if (!arr.find(({ id }) => id === val.id)) {
return arr.concat(val);
}
return arr;
}, []);
return data;
}, [data]);
return { data: realtimeData, isLoading, error };
return { data, isLoading, error };
}
export default useRealtime;

View file

@ -1,12 +1,12 @@
import { produce } from 'immer';
import { useCallback, useEffect, useState } from 'react';
import { useApi } from './useApi';
import { useApi } from '../useApi';
import { useTimezone } from '../useTimezone';
import { useMessages } from '../useMessages';
export function useReport(
reportId: string,
defaultParameters: { type: string; parameters: { [key: string]: any } },
defaultParameters?: { type: string; parameters: { [key: string]: any } },
) {
const [report, setReport] = useState(null);
const [isRunning, setIsRunning] = useState(false);

View file

@ -1,11 +1,11 @@
import useApi from './useApi';
import useFilterQuery from './useFilterQuery';
import useApi from '../useApi';
import usePagedQuery from '../usePagedQuery';
import useModified from '../useModified';
export function useReports({ websiteId, teamId }: { websiteId?: string; teamId?: string }) {
const { modified } = useModified(`reports`);
const { get, del, useMutation } = useApi();
const queryResult = useFilterQuery({
const queryResult = usePagedQuery({
queryKey: ['reports', { websiteId, teamId, modified }],
queryFn: (params: any) => {
return get('/reports', { websiteId, teamId, ...params });

View file

@ -0,0 +1,18 @@
import { useApi } from '../useApi';
export function useRevenueValues(websiteId: string, startDate: Date, endDate: Date) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: ['revenue:values', { websiteId, startDate, endDate }],
queryFn: () =>
get(`/reports/revenue`, {
websiteId,
startDate,
endDate,
}),
enabled: !!(websiteId && startDate && endDate),
});
}
export default useRevenueValues;

View file

@ -0,0 +1,21 @@
import { useApi } from '../useApi';
export function useSessionActivity(
websiteId: string,
sessionId: string,
startDate: Date,
endDate: Date,
) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: ['session:activity', { websiteId, sessionId, startDate, endDate }],
queryFn: () => {
return get(`/websites/${websiteId}/sessions/${sessionId}/activity`, {
startAt: +new Date(startDate),
endAt: +new Date(endDate),
});
},
enabled: Boolean(websiteId && sessionId && startDate && endDate),
});
}

View file

@ -0,0 +1,12 @@
import { useApi } from '../useApi';
export function useSessionData(websiteId: string, sessionId: string) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: ['session:data', { websiteId, sessionId }],
queryFn: () => {
return get(`/websites/${websiteId}/sessions/${sessionId}/properties`, { websiteId });
},
});
}

View file

@ -0,0 +1,20 @@
import { useApi } from '../useApi';
import { UseQueryOptions } from '@tanstack/react-query';
import { useFilterParams } from '../useFilterParams';
export function useSessionDataProperties(
websiteId: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
return useQuery<any>({
queryKey: ['websites:event-data:properties', { websiteId, ...params }],
queryFn: () => get(`/websites/${websiteId}/session-data/properties`, { ...params }),
enabled: !!websiteId,
...options,
});
}
export default useSessionDataProperties;

View file

@ -0,0 +1,21 @@
import { useApi } from '../useApi';
import { UseQueryOptions } from '@tanstack/react-query';
import { useFilterParams } from '../useFilterParams';
export function useSessionDataValues(
websiteId: string,
propertyName: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
return useQuery<any>({
queryKey: ['websites:session-data:values', { websiteId, propertyName, ...params }],
queryFn: () => get(`/websites/${websiteId}/session-data/values`, { ...params, propertyName }),
enabled: !!(websiteId && propertyName),
...options,
});
}
export default useSessionDataValues;

View file

@ -1,5 +1,5 @@
import useStore, { setShareToken } from 'store/app';
import useApi from './useApi';
import { useApi } from '../useApi';
const selector = (state: { shareToken: string }) => state.shareToken;

View file

@ -1,4 +1,4 @@
import useApi from './useApi';
import { useApi } from '../useApi';
export function useTeam(teamId: string) {
const { get, useQuery } = useApi();

View file

@ -1,12 +1,12 @@
import useApi from './useApi';
import useFilterQuery from './useFilterQuery';
import { useApi } from '../useApi';
import usePagedQuery from '../usePagedQuery';
import useModified from '../useModified';
export function useTeamMembers(teamId: string) {
const { get } = useApi();
const { modified } = useModified(`teams:members`);
return useFilterQuery({
return usePagedQuery({
queryKey: ['teams:members', { teamId, modified }],
queryFn: (params: any) => {
return get(`/teams/${teamId}/users`, params);

View file

@ -1,12 +1,12 @@
import useApi from './useApi';
import useFilterQuery from './useFilterQuery';
import { useApi } from '../useApi';
import { usePagedQuery } from '../usePagedQuery';
import useModified from '../useModified';
export function useTeamWebsites(teamId: string) {
const { get } = useApi();
const { modified } = useModified(`websites`);
return useFilterQuery({
return usePagedQuery({
queryKey: ['teams:websites', { teamId, modified }],
queryFn: (params: any) => {
return get(`/teams/${teamId}/websites`, params);

View file

@ -1,12 +1,12 @@
import useApi from './useApi';
import useFilterQuery from './useFilterQuery';
import { useApi } from '../useApi';
import { usePagedQuery } from '../usePagedQuery';
import useModified from '../useModified';
export function useTeams(userId: string) {
const { get } = useApi();
const { modified } = useModified(`teams`);
return useFilterQuery({
return usePagedQuery({
queryKey: ['teams', { userId, modified }],
queryFn: (params: any) => {
return get(`/users/${userId}/teams`, params);

View file

@ -1,4 +1,4 @@
import useApi from './useApi';
import { useApi } from '../useApi';
export function useUser(userId: string, options?: { [key: string]: any }) {
const { get, useQuery } = useApi();

View file

@ -1,12 +1,12 @@
import useApi from './useApi';
import useFilterQuery from './useFilterQuery';
import { useApi } from '../useApi';
import { usePagedQuery } from '../usePagedQuery';
import useModified from '../useModified';
export function useUsers() {
const { get } = useApi();
const { modified } = useModified(`users`);
return useFilterQuery({
return usePagedQuery({
queryKey: ['users', { modified }],
queryFn: (params: any) => {
return get('/admin/users', {

View file

@ -1,4 +1,4 @@
import useApi from './useApi';
import { useApi } from '../useApi';
export function useWebsite(websiteId: string, options?: { [key: string]: any }) {
const { get, useQuery } = useApi();

View file

@ -1,33 +1,19 @@
import useApi from './useApi';
import { useApi } from '../useApi';
import { UseQueryOptions } from '@tanstack/react-query';
import { useDateRange, useNavigation, useTimezone } from 'components/hooks';
import { zonedTimeToUtc } from 'date-fns-tz';
import { useFilterParams } from '../useFilterParams';
import { usePagedQuery } from '../usePagedQuery';
export function useWebsiteEvents(
websiteId: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, unit, offset } = dateRange;
const { timezone } = useTimezone();
const {
query: { url, event },
} = useNavigation();
const { get } = useApi();
const params = useFilterParams(websiteId);
const params = {
startAt: +zonedTimeToUtc(startDate, timezone),
endAt: +zonedTimeToUtc(endDate, timezone),
unit,
offset,
timezone,
url,
event,
};
return useQuery({
queryKey: ['events', { ...params }],
queryFn: () => get(`/websites/${websiteId}/events`, { ...params }),
return usePagedQuery({
queryKey: ['websites:events', { websiteId, ...params }],
queryFn: pageParams =>
get(`/websites/${websiteId}/events`, { ...params, ...pageParams, pageSize: 20 }),
enabled: !!websiteId,
...options,
});

View file

@ -0,0 +1,20 @@
import { useApi } from '../useApi';
import { UseQueryOptions } from '@tanstack/react-query';
import { useFilterParams } from '../useFilterParams';
export function useWebsiteEventsSeries(
websiteId: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
return useQuery({
queryKey: ['websites:events:series', { websiteId, ...params }],
queryFn: () => get(`/websites/${websiteId}/events/series`, { ...params }),
enabled: !!websiteId,
...options,
});
}
export default useWebsiteEventsSeries;

View file

@ -1,12 +1,16 @@
import useApi from './useApi';
import { UseQueryOptions } from '@tanstack/react-query';
import { useApi } from '../useApi';
import { useFilterParams } from '../useFilterParams';
import { useSearchParams } from 'next/navigation';
export function useWebsiteMetrics(
websiteId: string,
params?: { [key: string]: any },
queryParams: { type: string; limit?: number; search?: string; startAt?: number; endAt?: number },
options?: Omit<UseQueryOptions & { onDataLoad?: (data: any) => void }, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
const searchParams = useSearchParams();
return useQuery({
queryKey: [
@ -14,21 +18,21 @@ export function useWebsiteMetrics(
{
websiteId,
...params,
...queryParams,
},
],
queryFn: async () => {
const filters = { ...params };
filters[params.type] = undefined;
const data = await get(`/websites/${websiteId}/metrics`, {
...filters,
...params,
[searchParams.get('view')]: undefined,
...queryParams,
});
options?.onDataLoad?.(data);
return data;
},
enabled: !!websiteId,
...options,
});
}

View file

@ -1,34 +1,19 @@
import { zonedTimeToUtc } from 'date-fns-tz';
import { useApi, useDateRange, useNavigation, useTimezone } from 'components/hooks';
import { UseQueryOptions } from '@tanstack/react-query';
import { useApi } from '../useApi';
import { useFilterParams } from '..//useFilterParams';
export function useWebsitePageviews(websiteId: string, options?: { [key: string]: string }) {
export function useWebsitePageviews(
websiteId: string,
compare?: string,
options?: Omit<UseQueryOptions, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, unit } = dateRange;
const { timezone } = useTimezone();
const {
query: { url, referrer, os, browser, device, country, region, city, title },
} = useNavigation();
const params = {
startAt: +zonedTimeToUtc(startDate, timezone),
endAt: +zonedTimeToUtc(endDate, timezone),
unit,
timezone,
url,
referrer,
os,
browser,
device,
country,
region,
city,
title,
};
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,
});
}

View file

@ -0,0 +1,14 @@
import { useApi } from '../useApi';
export function useWebsiteSession(websiteId: string, sessionId: string) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: ['session', { websiteId, sessionId }],
queryFn: () => {
return get(`/websites/${websiteId}/sessions/${sessionId}`);
},
});
}
export default useWebsiteSession;

View file

@ -0,0 +1,16 @@
import { useApi } from '../useApi';
import { useFilterParams } from '../useFilterParams';
export function useWebsiteSessionStats(websiteId: string, options?: { [key: string]: string }) {
const { get, useQuery } = useApi();
const params = useFilterParams(websiteId);
return useQuery({
queryKey: ['sessions:stats', { websiteId, ...params }],
queryFn: () => get(`/websites/${websiteId}/sessions/stats`, { ...params }),
enabled: !!websiteId,
...options,
});
}
export default useWebsiteSessionStats;

View file

@ -0,0 +1,24 @@
import { useApi } from '../useApi';
import { usePagedQuery } from '../usePagedQuery';
import useModified from '../useModified';
import { useFilterParams } from 'components/hooks/useFilterParams';
export function useWebsiteSessions(websiteId: string, params?: { [key: string]: string | number }) {
const { get } = useApi();
const { modified } = useModified(`sessions`);
const filters = useFilterParams(websiteId);
return usePagedQuery({
queryKey: ['sessions', { websiteId, modified, ...params, ...filters }],
queryFn: (data: any) => {
return get(`/websites/${websiteId}/sessions`, {
...data,
...params,
...filters,
pageSize: 20,
});
},
});
}
export default useWebsiteSessions;

View file

@ -0,0 +1,24 @@
import { useApi } from '../useApi';
import useModified from '../useModified';
import { useFilterParams } from 'components/hooks/useFilterParams';
export function useWebsiteSessionsWeekly(
websiteId: string,
params?: { [key: string]: string | number },
) {
const { get, useQuery } = useApi();
const { modified } = useModified(`sessions`);
const filters = useFilterParams(websiteId);
return useQuery({
queryKey: ['sessions', { websiteId, modified, ...params, ...filters }],
queryFn: () => {
return get(`/websites/${websiteId}/sessions/weekly`, {
...params,
...filters,
});
},
});
}
export default useWebsiteSessionsWeekly;

View file

@ -1,30 +1,18 @@
import { useApi, useDateRange, useNavigation } from 'components/hooks';
import { useApi } from '../useApi';
import { useFilterParams } from '../useFilterParams';
export function useWebsiteStats(websiteId: string, options?: { [key: string]: string }) {
export function useWebsiteStats(
websiteId: string,
compare?: string,
options?: { [key: string]: string },
) {
const { get, useQuery } = useApi();
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate } = dateRange;
const {
query: { url, referrer, title, os, browser, device, country, region, city },
} = useNavigation();
const params = {
startAt: +startDate,
endAt: +endDate,
url,
referrer,
title,
os,
browser,
device,
country,
region,
city,
};
const params = useFilterParams(websiteId);
return useQuery({
queryKey: ['websites:stats', { websiteId, ...params }],
queryFn: () => get(`/websites/${websiteId}/stats`, params),
queryKey: ['websites:stats', { websiteId, ...params, compare }],
queryFn: () => get(`/websites/${websiteId}/stats`, { ...params, compare }),
enabled: !!websiteId,
...options,
});
}

View file

@ -1,4 +1,6 @@
import { useApi } from 'components/hooks';
import { useApi } from '../useApi';
import { useCountryNames, useRegionNames } from 'components/hooks';
import useLocale from '../useLocale';
export function useWebsiteValues({
websiteId,
@ -14,6 +16,36 @@ export function useWebsiteValues({
search?: string;
}) {
const { get, useQuery } = useApi();
const { locale } = useLocale();
const { countryNames } = useCountryNames(locale);
const { regionNames } = useRegionNames(locale);
const names = {
country: countryNames,
region: regionNames,
};
const getSearch = (type: string, value: string) => {
if (value) {
const values = names[type];
if (values) {
return (
Object.keys(values)
.reduce((arr: string[], key: string) => {
if (values[key].toLowerCase().includes(value.toLowerCase())) {
return arr.concat(key);
}
return arr;
}, [])
.slice(0, 5)
.join(',') || value
);
}
return value;
}
};
return useQuery({
queryKey: ['websites:values', { websiteId, type, startDate, endDate, search }],
@ -22,7 +54,7 @@ export function useWebsiteValues({
type,
startAt: +startDate,
endAt: +endDate,
search,
search: getSearch(type, search),
}),
enabled: !!(websiteId && type && startDate && endDate),
});

View file

@ -1,5 +1,5 @@
import { useApi } from './useApi';
import { useFilterQuery } from './useFilterQuery';
import { useApi } from '../useApi';
import { usePagedQuery } from '../usePagedQuery';
import { useLogin } from './useLogin';
import useModified from '../useModified';
@ -11,7 +11,7 @@ export function useWebsites(
const { user } = useLogin();
const { modified } = useModified(`websites`);
return useFilterQuery({
return usePagedQuery({
queryKey: ['websites', { userId, teamId, modified, ...params }],
queryFn: (data: any) => {
return get(teamId ? `/teams/${teamId}/websites` : `/users/${userId || user.id}/websites`, {

View file

@ -10,7 +10,7 @@ export function useCountryNames(locale: string) {
const [list, setList] = useState(countryNames[locale] || enUS);
async function loadData(locale: string) {
const { data } = await httpGet(`${process.env.basePath}/intl/country/${locale}.json`);
const { data } = await httpGet(`${process.env.basePath || ''}/intl/country/${locale}.json`);
if (data) {
countryNames[locale] = data;
@ -28,7 +28,7 @@ export function useCountryNames(locale: string) {
}
}, [locale]);
return list;
return { countryNames: list };
}
export default useCountryNames;

View file

@ -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';
import { useApi } from './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;

View file

@ -14,6 +14,8 @@ export function useFields() {
{ name: 'country', type: 'string', label: formatMessage(labels.country) },
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
{ name: 'host', type: 'string', label: formatMessage(labels.host) },
{ name: 'tag', type: 'string', label: formatMessage(labels.tag) },
];
return { fields };

View file

@ -0,0 +1,46 @@
import { useNavigation } from './useNavigation';
import { useDateRange } from './useDateRange';
import { useTimezone } from './useTimezone';
export function useFilterParams(websiteId: string) {
const { dateRange } = useDateRange(websiteId);
const { startDate, endDate, unit } = dateRange;
const { timezone, toUtc } = useTimezone();
const {
query: {
url,
referrer,
title,
query,
host,
os,
browser,
device,
country,
region,
city,
event,
tag,
},
} = useNavigation();
return {
startAt: +toUtc(startDate),
endAt: +toUtc(endDate),
unit,
timezone,
url,
referrer,
title,
query,
host,
os,
browser,
device,
country,
region,
city,
event,
tag,
};
}

View file

@ -8,8 +8,8 @@ import regions from '../../../public/iso-3166-2.json';
export function useFormat() {
const { formatMessage, labels } = useMessages();
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
const languageNames = useLanguageNames(locale);
const { countryNames } = useCountryNames(locale);
const { languageNames } = useLanguageNames(locale);
const formatOS = (value: string): string => {
return OS_NAMES[value] || value;

View file

@ -10,7 +10,7 @@ export function useLanguageNames(locale) {
const [list, setList] = useState(languageNames[locale] || enUS);
async function loadData(locale) {
const { data } = await httpGet(`${process.env.basePath}/intl/language/${locale}.json`);
const { data } = await httpGet(`${process.env.basePath || ''}/intl/language/${locale}.json`);
if (data) {
languageNames[locale] = data;
@ -28,7 +28,7 @@ export function useLanguageNames(locale) {
}
}, [locale]);
return list;
return { languageNames: list };
}
export default useLanguageNames;

View file

@ -19,7 +19,9 @@ export function useLocale() {
const dateLocale = getDateLocale(locale);
async function loadMessages(locale: string) {
const { ok, data } = await httpGet(`${process.env.basePath}/intl/messages/${locale}.json`);
const { ok, data } = await httpGet(
`${process.env.basePath || ''}/intl/messages/${locale}.json`,
);
if (ok) {
messages[locale] = data;

View file

@ -1,16 +1,18 @@
import { UseQueryOptions } from '@tanstack/react-query';
import { useState } from 'react';
import { PageResult, PageParams, PagedQueryResult } from 'lib/types';
import { useApi } from './useApi';
import { FilterResult, SearchFilter, FilterQueryResult } from 'lib/types';
import { useNavigation } from './useNavigation';
export function useFilterQuery<T = any>({
export function usePagedQuery<T = any>({
queryKey,
queryFn,
...options
}: Omit<UseQueryOptions, 'queryFn'> & { queryFn: (params?: object) => any }): FilterQueryResult<T> {
const [params, setParams] = useState<T | SearchFilter>({
}: Omit<UseQueryOptions, 'queryFn'> & { queryFn: (params?: object) => any }): PagedQueryResult<T> {
const { query: queryParams } = useNavigation();
const [params, setParams] = useState<PageParams>({
query: '',
page: 1,
page: +queryParams.page || 1,
});
const { useQuery } = useApi();
@ -21,11 +23,11 @@ export function useFilterQuery<T = any>({
});
return {
result: data as FilterResult<any>,
result: data as PageResult<T>,
query,
params,
setParams,
};
}
export default useFilterQuery;
export default usePagedQuery;

View file

@ -0,0 +1,19 @@
import useCountryNames from './useCountryNames';
import regions from '../../../public/iso-3166-2.json';
export function useRegionNames(locale: string) {
const { countryNames } = useCountryNames(locale);
const getRegionName = (regionCode: string, countryCode?: string) => {
if (!countryCode) {
return regions[regionCode];
}
const region = regionCode.includes('-') ? regionCode : `${countryCode}-${regionCode}`;
return regions[region] ? `${regions[region]}, ${countryNames[countryCode]}` : region;
};
return { regionNames: regions, getRegionName };
}
export default useRegionNames;

View file

@ -1,5 +1,6 @@
import { setItem } from 'next-basics';
import { TIMEZONE_CONFIG } from 'lib/constants';
import { formatInTimeZone, zonedTimeToUtc, utcToZonedTime } from 'date-fns-tz';
import useStore, { setTimezone } from 'store/app';
const selector = (state: { timezone: string }) => state.timezone;
@ -12,7 +13,25 @@ export function useTimezone() {
setTimezone(value);
};
return { timezone, saveTimezone };
const formatTimezoneDate = (date: string, pattern: string) => {
return formatInTimeZone(
/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$/.test(date)
? date
: date.split(' ').join('T') + 'Z',
timezone,
pattern,
);
};
const toUtc = (date: Date | string | number) => {
return zonedTimeToUtc(date, timezone);
};
const fromUtc = (date: Date | string | number) => {
return utcToZonedTime(date, timezone);
};
return { timezone, saveTimezone, formatTimezoneDate, toUtc, fromUtc };
}
export default useTimezone;