mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-05-07 20:32:26 +00:00
556bbedeca
add request classes and fix collection typing for clients, tasks and tags
319 lines
10 KiB
TypeScript
319 lines
10 KiB
TypeScript
import type { QueryClient } from '@tanstack/vue-query';
|
|
import { api } from '@/packages/api/src';
|
|
import { getCurrentOrganizationId, getCurrentMembershipId } from '@/utils/useUser';
|
|
import { canViewClients, canViewMembers } from '@/utils/permissions';
|
|
import {
|
|
getInitialWeekRange,
|
|
getExpandedCalendarDateRange,
|
|
createCalendarQueryKey,
|
|
fetchAllCalendarEntries,
|
|
} from '@/utils/useTimeEntriesCalendarQuery';
|
|
import { fetchAllProjects } from '@/utils/useProjectsQuery';
|
|
import { fetchAllTasks } from '@/utils/useTasksQuery';
|
|
import { fetchAllTags } from '@/utils/useTagsQuery';
|
|
import { fetchAllClients } from '@/utils/useClientsQuery';
|
|
import { fetchAllMembers } from '@/utils/useMembersQuery';
|
|
import { fetchAllReports } from '@/utils/useReportsQuery';
|
|
import { fetchAllProjectMembers } from '@/utils/useProjectMembersQuery';
|
|
|
|
/**
|
|
* Route patterns mapped to their prefetch functions.
|
|
* Each function receives the QueryClient and prefetches relevant data.
|
|
*/
|
|
const routePrefetchers: Record<string, (queryClient: QueryClient) => void> = {
|
|
'/': (queryClient) => {
|
|
prefetchDashboard(queryClient);
|
|
},
|
|
|
|
'/dashboard': (queryClient) => {
|
|
prefetchDashboard(queryClient);
|
|
},
|
|
|
|
'/time': (queryClient) => {
|
|
prefetchProjects(queryClient);
|
|
prefetchTasks(queryClient);
|
|
prefetchTags(queryClient);
|
|
prefetchClients(queryClient);
|
|
prefetchTimeEntries(queryClient);
|
|
},
|
|
|
|
'/calendar': (queryClient) => {
|
|
prefetchProjects(queryClient);
|
|
prefetchTasks(queryClient);
|
|
prefetchTags(queryClient);
|
|
prefetchClients(queryClient);
|
|
prefetchCalendarTimeEntries(queryClient);
|
|
},
|
|
|
|
'/projects': (queryClient) => {
|
|
prefetchProjects(queryClient);
|
|
prefetchClients(queryClient);
|
|
},
|
|
|
|
'/clients': (queryClient) => {
|
|
prefetchClients(queryClient);
|
|
},
|
|
|
|
'/tags': (queryClient) => {
|
|
prefetchTags(queryClient);
|
|
},
|
|
|
|
'/members': (queryClient) => {
|
|
prefetchMembers(queryClient);
|
|
},
|
|
|
|
'/reporting': (queryClient) => {
|
|
prefetchProjects(queryClient);
|
|
prefetchTags(queryClient);
|
|
prefetchClients(queryClient);
|
|
prefetchMembers(queryClient);
|
|
},
|
|
|
|
'/reporting/detailed': (queryClient) => {
|
|
prefetchProjects(queryClient);
|
|
prefetchTasks(queryClient);
|
|
prefetchTags(queryClient);
|
|
prefetchClients(queryClient);
|
|
prefetchMembers(queryClient);
|
|
},
|
|
|
|
'/reporting/shared': (queryClient) => {
|
|
prefetchReports(queryClient);
|
|
},
|
|
};
|
|
|
|
function prefetchDashboard(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId) return;
|
|
|
|
// Prefetch projects and tasks for RecentlyTrackedTasksCard
|
|
prefetchProjects(queryClient);
|
|
prefetchTasks(queryClient);
|
|
|
|
// Prefetch all dashboard card data
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['timeEntries', organizationId],
|
|
queryFn: () =>
|
|
api.getTimeEntries({
|
|
params: { organization: organizationId },
|
|
queries: { limit: 10, offset: 0, only_full_dates: 'true' },
|
|
}),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['lastSevenDays', organizationId],
|
|
queryFn: () => api.lastSevenDays({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['dailyTrackedHours', organizationId],
|
|
queryFn: () => api.dailyTrackedHours({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['weeklyProjectOverview', organizationId],
|
|
queryFn: () => api.weeklyProjectOverview({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['totalWeeklyTime', organizationId],
|
|
queryFn: () => api.totalWeeklyTime({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['totalWeeklyBillableTime', organizationId],
|
|
queryFn: () => api.totalWeeklyBillableTime({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['totalWeeklyBillableAmount', organizationId],
|
|
queryFn: () => api.totalWeeklyBillableAmount({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['weeklyHistory', organizationId],
|
|
queryFn: () => api.weeklyHistory({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
|
|
// Prefetch team activity only if user has permission
|
|
if (canViewMembers()) {
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['latestTeamActivity', organizationId],
|
|
queryFn: () => api.latestTeamActivity({ params: { organization: organizationId } }),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
}
|
|
|
|
function prefetchProjects(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId) return;
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['projects', organizationId],
|
|
queryFn: async () => ({ data: await fetchAllProjects(organizationId) }),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchTasks(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId) return;
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['tasks', organizationId],
|
|
queryFn: async () => ({ data: await fetchAllTasks(organizationId) }),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchTags(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId) return;
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['tags', organizationId],
|
|
queryFn: async () => ({ data: await fetchAllTags(organizationId) }),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchClients(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId || !canViewClients()) return;
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['clients', organizationId],
|
|
queryFn: async () => ({ data: await fetchAllClients(organizationId) }),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchMembers(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId || !canViewMembers()) return;
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['members', organizationId],
|
|
queryFn: async () => ({ data: await fetchAllMembers(organizationId) }),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchReports(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId) return;
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['reports', organizationId],
|
|
queryFn: async () => ({ data: await fetchAllReports(organizationId) }),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchTimeEntries(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
const memberId = getCurrentMembershipId();
|
|
if (!organizationId) return;
|
|
|
|
queryClient.prefetchInfiniteQuery({
|
|
queryKey: ['timeEntries', 'infinite', { organizationId, memberId }],
|
|
queryFn: async () => {
|
|
const response = await api.getTimeEntries({
|
|
params: { organization: organizationId },
|
|
queries: {
|
|
only_full_dates: 'true',
|
|
member_id: memberId,
|
|
},
|
|
});
|
|
return response;
|
|
},
|
|
initialPageParam: undefined,
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchCalendarTimeEntries(queryClient: QueryClient) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
const memberId = getCurrentMembershipId();
|
|
if (!organizationId) return;
|
|
|
|
const { start, end } = getInitialWeekRange();
|
|
const { start: formattedStart, end: formattedEnd } = getExpandedCalendarDateRange(start, end);
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: createCalendarQueryKey(formattedStart, formattedEnd, organizationId),
|
|
queryFn: () =>
|
|
fetchAllCalendarEntries(organizationId, memberId, formattedStart, formattedEnd),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
function prefetchProjectMembers(queryClient: QueryClient, projectId: string) {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (!organizationId || !canViewMembers()) return;
|
|
|
|
queryClient.prefetchQuery({
|
|
queryKey: ['projectMembers', organizationId, projectId],
|
|
queryFn: async () => ({
|
|
data: await fetchAllProjectMembers(organizationId, projectId),
|
|
}),
|
|
staleTime: 30000,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Matches a URL to find the appropriate prefetcher.
|
|
* Handles both exact matches and pattern matching for dynamic routes.
|
|
*/
|
|
function findPrefetcher(url: string): ((queryClient: QueryClient) => void) | undefined {
|
|
// Extract pathname from URL
|
|
const pathname = url.startsWith('http') ? new URL(url).pathname : url.split('?')[0]!;
|
|
|
|
// Try exact match first
|
|
if (pathname && routePrefetchers[pathname]) {
|
|
return routePrefetchers[pathname];
|
|
}
|
|
|
|
// Try pattern matching for dynamic routes like /projects/{id}
|
|
const projectMatch = pathname?.match(/^\/projects\/([^/]+)$/);
|
|
if (projectMatch) {
|
|
const projectId = projectMatch[1]!;
|
|
return (queryClient) => {
|
|
prefetchProjects(queryClient);
|
|
prefetchTasks(queryClient);
|
|
prefetchProjectMembers(queryClient, projectId);
|
|
};
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Sets up Inertia prefetch event listener to warm TanStack Query cache.
|
|
* Call this once during app initialization.
|
|
*/
|
|
export function setupPrefetching(queryClient: QueryClient) {
|
|
// Listen for the 'prefetching' event which fires when Inertia starts prefetching a page
|
|
// The event detail contains the visit object with the URL being prefetched
|
|
document.addEventListener('inertia:prefetching', ((event: CustomEvent) => {
|
|
const visit = event.detail?.visit;
|
|
if (!visit?.url) return;
|
|
|
|
const url = visit.url.href || visit.url.toString();
|
|
const prefetcher = findPrefetcher(url);
|
|
|
|
if (prefetcher) {
|
|
prefetcher(queryClient);
|
|
}
|
|
}) as EventListener);
|
|
}
|