Merge branch 'dev' into os-names

This commit is contained in:
Mike Cao 2024-02-14 22:15:08 -08:00 committed by GitHub
commit 37e28bbf74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
472 changed files with 5460 additions and 5026 deletions

View file

@ -1,22 +0,0 @@
export * from './useApi';
export * from './useConfig';
export * from './useCountryNames';
export * from './useDateRange';
export * from './useDocumentClick';
export * from './useEscapeKey';
export * from './useFilters';
export * from './useForceUpdate';
export * from './useFormat';
export * from './useLanguageNames';
export * from './useLocale';
export * from './useMessages';
export * from './useNavigation';
export * from './useReport';
export * from './useReports';
export * from './useLogin';
export * from './useShareToken';
export * from './useSticky';
export * from './useTheme';
export * from './useTimezone';
export * from './useUser';
export * from './useWebsite';

View file

@ -0,0 +1,33 @@
export * from './queries/useApi';
export * from './queries/useConfig';
export * from './queries/useFilterQuery';
export * from './queries/useLogin';
export * from './queries/useReport';
export * from './queries/useReports';
export * from './queries/useShareToken';
export * from './queries/useTeam';
export * from './queries/useTeams';
export * from './queries/useTeamWebsites';
export * from './queries/useTeamMembers';
export * from './queries/useUser';
export * from './queries/useUsers';
export * from './queries/useWebsite';
export * from './queries/useWebsites';
export * from './queries/useWebsiteEvents';
export * from './queries/useWebsiteMetrics';
export * from './useCountryNames';
export * from './useDateRange';
export * from './useDocumentClick';
export * from './useEscapeKey';
export * from './useFilters';
export * from './useForceUpdate';
export * from './useFormat';
export * from './useLanguageNames';
export * from './useLocale';
export * from './useMessages';
export * from './useModified';
export * from './useNavigation';
export * from './useSticky';
export * from './useTeamUrl';
export * from './useTheme';
export * from './useTimezone';

View file

@ -4,7 +4,7 @@ import { getClientAuthToken } from 'lib/client';
import { SHARE_TOKEN_HEADER } from 'lib/constants';
import useStore from 'store/app';
const selector = state => state.shareToken;
const selector = (state: { shareToken: { token?: string } }) => state.shareToken;
export function useApi() {
const shareToken = useStore(selector);

View file

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

View file

@ -1,16 +1,9 @@
import { UseQueryOptions } from '@tanstack/react-query';
import { useState, Dispatch, SetStateAction } from 'react';
import { useApi } from 'components/hooks/useApi';
import { FilterResult, SearchFilter } from 'lib/types';
import { useState } from 'react';
import { useApi } from './useApi';
import { FilterResult, SearchFilter, FilterQueryResult } from 'lib/types';
export interface FilterQueryResult<T> {
result: FilterResult<T>;
query: any;
params: SearchFilter;
setParams: Dispatch<SetStateAction<T | SearchFilter>>;
}
export function useFilterQuery<T>({
export function useFilterQuery<T = any>({
queryKey,
queryFn,
...options

View file

@ -0,0 +1,29 @@
import useStore, { setUser } from 'store/app';
import useApi from './useApi';
import { UseQueryResult } from '@tanstack/react-query';
const selector = (state: { user: any }) => state.user;
export function useLogin(): {
user: any;
setUser: (data: any) => void;
} & UseQueryResult {
const { get, useQuery } = useApi();
const user = useStore(selector);
const query = useQuery({
queryKey: ['login'],
queryFn: async () => {
const data = await get('/auth/verify');
setUser(data);
return data;
},
enabled: !user,
});
return { user, setUser, ...query };
}
export default useLogin;

View file

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

View file

@ -0,0 +1,28 @@
import useApi from './useApi';
import useFilterQuery from './useFilterQuery';
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({
queryKey: ['reports', { websiteId, teamId, modified }],
queryFn: (params: any) => {
return get('/reports', { websiteId, teamId, ...params });
},
});
const { mutate } = useMutation({ mutationFn: (reportId: string) => del(`/reports/${reportId}`) });
const deleteReport = (reportId: any) => {
mutate(reportId, {
onSuccess: () => {},
});
};
return {
...queryResult,
deleteReport,
};
}
export default useReports;

View file

@ -0,0 +1,12 @@
import useApi from './useApi';
export function useTeam(teamId: string) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: ['teams', teamId],
queryFn: () => get(`/teams/${teamId}`),
enabled: !!teamId,
});
}
export default useTeam;

View file

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

View file

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

View file

@ -0,0 +1,20 @@
import useApi from './useApi';
import useFilterQuery from './useFilterQuery';
import useLogin from './useLogin';
import useModified from '../useModified';
export function useTeams(userId?: string) {
const { get } = useApi();
const { user } = useLogin();
const id = userId || user?.id;
const { modified } = useModified(`teams`);
return useFilterQuery({
queryKey: ['teams', { userId: id, modified }],
queryFn: (params: any) => {
return get(`/teams`, params);
},
});
}
export default useTeams;

View file

@ -0,0 +1,13 @@
import useApi from './useApi';
export function useUser(userId: string, options?: { [key: string]: any }) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: ['users', userId],
queryFn: () => get(`/users/${userId}`),
enabled: !!userId,
...options,
});
}
export default useUser;

View file

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

View file

@ -1,11 +1,13 @@
import useApi from './useApi';
export function useWebsite(websiteId: string) {
export function useWebsite(websiteId: string, options?: { [key: string]: any }) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: ['websites', websiteId],
queryKey: ['website', { websiteId }],
queryFn: () => get(`/websites/${websiteId}`),
enabled: !!websiteId,
...options,
});
}

View file

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

View file

@ -0,0 +1,36 @@
import useApi from './useApi';
import { UseQueryOptions } from '@tanstack/react-query';
export function useWebsiteMetrics(
websiteId: string,
params?: { [key: string]: any },
options?: Omit<UseQueryOptions & { onDataLoad?: (data: any) => void }, 'queryKey' | 'queryFn'>,
) {
const { get, useQuery } = useApi();
return useQuery({
queryKey: [
'websites:metrics',
{
websiteId,
...params,
},
],
queryFn: async () => {
const filters = { ...params };
filters[params.type] = undefined;
const data = await get(`/websites/${websiteId}/metrics`, {
...filters,
});
options?.onDataLoad?.(data);
return data;
},
...options,
});
}
export default useWebsiteMetrics;

View file

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

View file

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { httpGet } from 'next-basics';
import enUS from 'public/intl/country/en-US.json';
import enUS from '../../../public/intl/country/en-US.json';
const countryNames = {
'en-US': enUS,

View file

@ -4,10 +4,10 @@ import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
import websiteStore, { setWebsiteDateRange } from 'store/websites';
import appStore, { setDateRange } from 'store/app';
import { DateRange } from 'lib/types';
import useLocale from './useLocale';
import useApi from './useApi';
import { useLocale } from './useLocale';
import { useApi } from './queries/useApi';
export function useDateRange(websiteId?: string) {
export function useDateRange(websiteId?: string): [DateRange, (value: string | DateRange) => void] {
const { get } = useApi();
const { locale } = useLocale();
const websiteConfig = websiteStore(state => state[websiteId]?.dateRange);
@ -45,10 +45,7 @@ export function useDateRange(websiteId?: string) {
}
};
return [dateRange, saveDateRange] as [
{ startDate: Date; endDate: Date; modified?: number },
(value: string | DateRange) => void,
];
return [dateRange, saveDateRange];
}
export default useDateRange;

View file

@ -2,7 +2,7 @@ import useMessages from './useMessages';
import { BROWSERS, OS_NAMES } from 'lib/constants';
import useLocale from './useLocale';
import useCountryNames from './useCountryNames';
import regions from 'public/iso-3166-2.json';
import regions from '../../../public/iso-3166-2.json';
export function useFormat() {
const { formatMessage, labels } = useMessages();
@ -31,7 +31,7 @@ export function useFormat() {
};
const formatCity = (value: string, country?: string): string => {
return `${value}, ${countryNames[country]}`;
return countryNames[country] ? `${value}, ${countryNames[country]}` : value;
};
const formatValue = (value: string, type: string, data?: { [key: string]: any }): string => {

View file

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { httpGet } from 'next-basics';
import enUS from 'public/intl/language/en-US.json';
import enUS from '../../../public/intl/language/en-US.json';
const languageNames = {
'en-US': enUS,

View file

@ -3,14 +3,14 @@ import { httpGet, setItem } from 'next-basics';
import { LOCALE_CONFIG } from 'lib/constants';
import { getDateLocale, getTextDirection } from 'lib/lang';
import useStore, { setLocale } from 'store/app';
import useForceUpdate from 'components/hooks/useForceUpdate';
import enUS from 'public/intl/country/en-US.json';
import { useForceUpdate } from './useForceUpdate';
import enUS from '../../../public/intl/country/en-US.json';
const messages = {
'en-US': enUS,
};
const selector = state => state.locale;
const selector = (state: { locale: any }) => state.locale;
export function useLocale() {
const locale = useStore(selector);
@ -18,7 +18,7 @@ export function useLocale() {
const dir = getTextDirection(locale);
const dateLocale = getDateLocale(locale);
async function loadMessages(locale) {
async function loadMessages(locale: string) {
const { ok, data } = await httpGet(`${process.env.basePath}/intl/messages/${locale}.json`);
if (ok) {
@ -26,7 +26,7 @@ export function useLocale() {
}
}
async function saveLocale(value) {
async function saveLocale(value: string) {
if (!messages[value]) {
await loadMessages(value);
}

View file

@ -1,22 +0,0 @@
import useApi from 'components/hooks/useApi';
import useUser from 'components/hooks/useUser';
export function useLogin() {
const { get, useQuery } = useApi();
const { user, setUser } = useUser();
const query = useQuery({
queryKey: ['login'],
queryFn: async () => {
const data = await get('/auth/verify');
setUser(data);
return data;
},
});
return { user, ...query };
}
export default useLogin;

View file

@ -1,6 +1,5 @@
import { useIntl, FormattedMessage, MessageDescriptor, PrimitiveType } from 'react-intl';
import { useIntl, FormattedMessage } from 'react-intl';
import { messages, labels } from 'components/messages';
import { FormatXMLElementFn, Options } from 'intl-messageformat';
export function useMessages(): any {
const intl = useIntl();
@ -12,14 +11,12 @@ export function useMessages(): any {
};
const formatMessage = (
descriptor:
| MessageDescriptor
| {
id: string;
defaultMessage: string;
},
values?: Record<string, PrimitiveType | FormatXMLElementFn<string, string>>,
opts?: Options,
descriptor: {
id: string;
defaultMessage: string;
},
values?: { [key: string]: string },
opts?: any,
) => {
return descriptor ? intl.formatMessage(descriptor, values, opts) : null;
};

View file

@ -0,0 +1,15 @@
import useStore from 'store/modified';
export function useModified(key?: string) {
const modified = useStore(state => state?.[key]);
const touch = (id?: string) => {
if (id || key) {
useStore.setState({ [id || key]: Date.now() });
}
};
return { modified, touch };
}
export default useModified;

View file

@ -6,7 +6,7 @@ export function useNavigation(): {
pathname: string;
query: { [key: string]: string };
router: any;
makeUrl: (params: any, reset?: boolean) => string;
renderUrl: (params: any, reset?: boolean) => string;
} {
const router = useRouter();
const pathname = usePathname();
@ -22,11 +22,11 @@ export function useNavigation(): {
return obj;
}, [params]);
function makeUrl(params: any, reset?: boolean) {
function renderUrl(params: any, reset?: boolean) {
return reset ? pathname : buildUrl(pathname, { ...query, ...params });
}
return { pathname, query, router, makeUrl };
return { pathname, query, router, renderUrl };
}
export default useNavigation;

View file

@ -1,30 +0,0 @@
import { useState } from 'react';
import useApi from './useApi';
import useFilterQuery from 'components/hooks/useFilterQuery';
export function useReports(websiteId?: string) {
const [modified, setModified] = useState(Date.now());
const { get, del, useMutation } = useApi();
const { mutate } = useMutation({ mutationFn: (reportId: string) => del(`/reports/${reportId}`) });
const queryResult = useFilterQuery({
queryKey: ['reports', { websiteId, modified }],
queryFn: (params: any) => {
return get(websiteId ? `/websites/${websiteId}/reports` : `/reports`, params);
},
});
const deleteReport = (id: any) => {
mutate(id, {
onSuccess: () => {
setModified(Date.now());
},
});
};
return {
...queryResult,
deleteReport,
};
}
export default useReports;

View file

@ -0,0 +1,17 @@
import { usePathname } from 'next/navigation';
export function useTeamUrl(): {
teamId?: string;
renderTeamUrl: (url: string) => string;
} {
const pathname = usePathname();
const [, teamId] = pathname.match(/^\/teams\/([a-f0-9-]+)/) || [];
function renderTeamUrl(url: string) {
return teamId ? `/teams/${teamId}${url}` : url;
}
return { teamId, renderTeamUrl };
}
export default useTeamUrl;

View file

@ -7,7 +7,7 @@ export function useTimezone() {
const [timezone, setTimezone] = useState(getItem(TIMEZONE_CONFIG) || getTimezone());
const saveTimezone = useCallback(
value => {
(value: string) => {
setItem(TIMEZONE_CONFIG, value);
setTimezone(value);
},

View file

@ -1,11 +0,0 @@
import useStore, { setUser } from 'store/app';
const selector = state => state.user;
export function useUser() {
const user = useStore(selector);
return { user, setUser };
}
export default useUser;