diff --git a/app/(dashboard)/[tenant]/projects/[projectId]/layout.tsx b/app/(dashboard)/[tenant]/projects/[projectId]/layout.tsx index f557082..eaac17f 100644 --- a/app/(dashboard)/[tenant]/projects/[projectId]/layout.tsx +++ b/app/(dashboard)/[tenant]/projects/[projectId]/layout.tsx @@ -1,12 +1,16 @@ "use client"; import { TaskListsProvider } from "@/hooks/use-tasklist"; +import { useProjectPrefetch } from "@/hooks/use-project-prefetch"; import { useParams } from "next/navigation"; export default function Layout({ children }: { children: React.ReactNode }) { - const { projectId } = useParams(); + const params = useParams(); + const projectId = +params.projectId!; + + useProjectPrefetch(projectId); return ( - {children} + {children} ); } diff --git a/bun.lock b/bun.lock index 8592883..f0fb952 100644 --- a/bun.lock +++ b/bun.lock @@ -41,7 +41,7 @@ "@upstash/search": "^0.1.5", "@upstash/workflow": "^0.2.16", "autoprefixer": "10.4.14", - "caniuse-lite": "^1.0.30001735", + "caniuse-lite": "^1.0.30001737", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -1218,7 +1218,7 @@ "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], - "caniuse-lite": ["caniuse-lite@1.0.30001735", "", {}, "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w=="], + "caniuse-lite": ["caniuse-lite@1.0.30001737", "", {}, "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], diff --git a/components/form/task.tsx b/components/form/task.tsx index 4768345..2dc7bbc 100644 --- a/components/form/task.tsx +++ b/components/form/task.tsx @@ -18,8 +18,7 @@ export default function InlineTaskForm({ const [value, setValue] = useState(""); const inputRef = useRef(null); - const openCreate = useCallback(() => setIsCreating(true), []); - useKeyboardShortcut("n", () => openCreate, !isCreating); + useKeyboardShortcut("n", () => setIsCreating(true)); const handleSubmit = useCallback(async () => { await action(value); diff --git a/components/project/events/full-calendar.tsx b/components/project/events/full-calendar.tsx index 6d1d698..800b409 100644 --- a/components/project/events/full-calendar.tsx +++ b/components/project/events/full-calendar.tsx @@ -74,8 +74,7 @@ export function FullCalendar({ parseAsBoolean.withDefault(false), ); - const openCreate = useCallback(() => setCreate(true), [setCreate]); - useKeyboardShortcut("n", () => openCreate); + useKeyboardShortcut("n", () => setCreate(true)); const [selectedDay, setSelectedDay] = useState(today); const [currentMonth, setCurrentMonth] = useState(format(today, "MMM-yyyy")); diff --git a/hooks/use-project-prefetch.ts b/hooks/use-project-prefetch.ts new file mode 100644 index 0000000..4fcc863 --- /dev/null +++ b/hooks/use-project-prefetch.ts @@ -0,0 +1,59 @@ +"use client"; + +import { useTRPC } from "@/trpc/client"; +import { useQueryClient } from "@tanstack/react-query"; +import { useEffect } from "react"; + +export function useProjectPrefetch(projectId: number) { + const trpc = useTRPC(); + const queryClient = useQueryClient(); + + useEffect(() => { + if (!projectId) return; + + const prefetchData = async () => { + const baseQueries = [ + queryClient.prefetchQuery( + trpc.events.getByMonth.queryOptions({ + projectId, + date: new Date(), + }), + ), + queryClient.prefetchQuery( + trpc.projects.getProjectById.queryOptions({ + id: projectId, + }), + ), + ]; + + const taskListsQuery = queryClient.prefetchQuery( + trpc.tasks.getTaskLists.queryOptions({ + projectId, + }), + ); + + await Promise.allSettled([...baseQueries, taskListsQuery]); + + const taskListsData = queryClient.getQueryData( + trpc.tasks.getTaskLists.queryKey({ + projectId, + }), + ); + + if (taskListsData) { + const individualTaskListQueries = taskListsData.map((taskList) => + queryClient.prefetchQuery( + trpc.tasks.getListById.queryOptions({ + id: taskList.id, + }), + ), + ); + + await Promise.allSettled(individualTaskListQueries); + } + }; + + prefetchData(); + }, [projectId, queryClient, trpc]); +} + diff --git a/package.json b/package.json index bbeb017..bd7bc2e 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "@upstash/search": "^0.1.5", "@upstash/workflow": "^0.2.16", "autoprefixer": "10.4.14", - "caniuse-lite": "^1.0.30001735", + "caniuse-lite": "^1.0.30001737", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/trpc/routers/tasks.ts b/trpc/routers/tasks.ts index 817029c..d0cf863 100644 --- a/trpc/routers/tasks.ts +++ b/trpc/routers/tasks.ts @@ -31,9 +31,9 @@ export const tasksRouter = createTRPCRouter({ const hasAccess = await canViewProject(ctx, input.projectId); if (!hasAccess) { throw new TRPCError({ - code: "FORBIDDEN", - message: "Project access denied", - }); + code: "FORBIDDEN", + message: "Project access denied", + }); } const data = await ctx.db.query.taskList