New admin section.

This commit is contained in:
Mike Cao 2025-07-08 18:40:47 -07:00
parent b78ff3b477
commit ce1f6c3618
44 changed files with 515 additions and 157 deletions

View file

@ -19,6 +19,8 @@ export * from './queries/useWebsiteSessionsWeeklyQuery';
export * from './queries/useShareTokenQuery';
export * from './queries/useTeamQuery';
export * from './queries/useTeamsQuery';
export * from './queries/useUserTeamsQuery';
export * from './queries/useUserWebsitesQuery';
export * from './queries/useTeamWebsitesQuery';
export * from './queries/useTeamMembersQuery';
export * from './queries/useUserQuery';

View file

@ -1,15 +1,20 @@
import { useApi } from '../useApi';
import { useModified } from '../useModified';
import { usePagedQuery } from '@/components/hooks';
import { ReactQueryOptions } from '@/lib/types';
export function useTeamsQuery(userId: string) {
const { get, useQuery } = useApi();
export function useTeamsQuery(params?: Record<string, any>, options?: ReactQueryOptions) {
const { get } = useApi();
const { modified } = useModified(`teams`);
return useQuery({
queryKey: ['teams', { userId, modified }],
queryFn: () => {
return get(`/users/${userId}/teams`, { userId });
return usePagedQuery({
queryKey: ['websites', { modified, ...params }],
queryFn: pageParams => {
return get(`/admin/teams`, {
...params,
...pageParams,
});
},
enabled: !!userId,
...options,
});
}

View file

@ -0,0 +1,15 @@
import { useApi } from '../useApi';
import { useModified } from '../useModified';
export function useUserTeamsQuery(userId: string) {
const { get, useQuery } = useApi();
const { modified } = useModified(`teams`);
return useQuery({
queryKey: ['teams', { userId, modified }],
queryFn: () => {
return get(`/users/${userId}/teams`, { userId });
},
enabled: !!userId,
});
}

View file

@ -0,0 +1,26 @@
import { useApi } from '../useApi';
import { usePagedQuery } from '../usePagedQuery';
import { useLoginQuery } from './useLoginQuery';
import { useModified } from '../useModified';
import { ReactQueryOptions } from '@/lib/types';
export function useUserWebsitesQuery(
{ userId, teamId }: { userId?: string; teamId?: string },
params?: Record<string, any>,
options?: ReactQueryOptions,
) {
const { get } = useApi();
const { user } = useLoginQuery();
const { modified } = useModified(`websites`);
return usePagedQuery({
queryKey: ['websites', { userId, teamId, modified, ...params }],
queryFn: pageParams => {
return get(teamId ? `/teams/${teamId}/websites` : `/users/${userId || user.id}/websites`, {
...params,
...pageParams,
});
},
...options,
});
}

View file

@ -8,9 +8,9 @@ export function useUsersQuery() {
return usePagedQuery({
queryKey: ['users', { modified }],
queryFn: (params: any) => {
queryFn: (pageParams: any) => {
return get('/admin/users', {
...params,
...pageParams,
});
},
});

View file

@ -1,24 +1,18 @@
import { useApi } from '../useApi';
import { usePagedQuery } from '../usePagedQuery';
import { useLoginQuery } from './useLoginQuery';
import { useModified } from '../useModified';
import { ReactQueryOptions } from '@/lib/types';
export function useWebsitesQuery(
{ userId, teamId }: { userId?: string; teamId?: string },
params?: Record<string, any>,
options?: ReactQueryOptions,
) {
export function useWebsitesQuery(params?: Record<string, any>, options?: ReactQueryOptions) {
const { get } = useApi();
const { user } = useLoginQuery();
const { modified } = useModified(`websites`);
return usePagedQuery({
queryKey: ['websites', { userId, teamId, modified, ...params }],
queryKey: ['websites', { modified, ...params }],
queryFn: pageParams => {
return get(teamId ? `/teams/${teamId}/websites` : `/users/${userId || user.id}/websites`, {
...params,
return get(`/admin/websites`, {
...pageParams,
...params,
});
},
...options,

View file

@ -1,3 +1,4 @@
import { useState, useEffect } from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { buildUrl } from '@/lib/url';
@ -7,18 +8,31 @@ export function useNavigation() {
const searchParams = useSearchParams();
const [, teamId] = pathname.match(/\/teams\/([a-f0-9-]+)/) || [];
const [, websiteId] = pathname.match(/\/websites\/([a-f0-9-]+)/) || [];
const query = Object.fromEntries(searchParams);
const [queryParams, setQueryParams] = useState(Object.fromEntries(searchParams));
const updateParams = (params?: Record<string, string | number>) => {
return !params ? pathname : buildUrl(pathname, { ...query, ...params });
return !params ? pathname : buildUrl(pathname, { ...queryParams, ...params });
};
useEffect(() => {
setQueryParams(Object.fromEntries(searchParams));
}, [searchParams.toString()]);
const renderUrl = (path: string, params?: Record<string, string | number> | false) => {
return buildUrl(
teamId ? `/teams/${teamId}${path}` : path,
params === false ? {} : { ...query, ...params },
params === false ? {} : { ...queryParams, ...params },
);
};
return { router, pathname, searchParams, query, teamId, websiteId, updateParams, renderUrl };
return {
router,
pathname,
searchParams,
query: queryParams,
teamId,
websiteId,
updateParams,
renderUrl,
};
}

View file

@ -1,28 +1,27 @@
import { UseQueryOptions } from '@tanstack/react-query';
import { useState } from 'react';
import { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
import { useApi } from './useApi';
import { useNavigation } from './useNavigation';
import { PageResult } from '@/lib/types';
export function usePagedQuery({
export function usePagedQuery<TData = any, TError = Error>({
queryKey,
queryFn,
...options
}: Omit<UseQueryOptions, 'queryFn'> & { queryFn: (params?: object) => any }) {
const { query: queryParams } = useNavigation();
const [params, setParams] = useState({
search: queryParams?.search ?? '',
page: queryParams?.page ?? '1',
});
}: Omit<
UseQueryOptions<PageResult<TData>, TError, PageResult<TData>, readonly unknown[]>,
'queryFn' | 'queryKey'
> & {
queryKey: readonly unknown[];
queryFn: (params?: object) => Promise<PageResult<TData>> | PageResult<TData>;
}): UseQueryResult<PageResult<TData>, TError> {
const {
query: { page, search },
} = useNavigation();
const { useQuery } = useApi();
return {
...useQuery({
queryKey: [{ ...queryKey, ...params }],
queryFn: () => queryFn(params),
...options,
}),
params,
setParams,
};
return useQuery<PageResult<TData>, TError>({
queryKey: [...queryKey, page, search] as const,
queryFn: () => queryFn({ page, search }),
...options,
});
}