From cf5597d541fe7dbc0c9ccb3983292faf7f3f4ad8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 7 Sep 2026 07:27:12 +0000 Subject: [PATCH] Remove playground Apollo; tRPC LeetCode import; profile chrome layout - Add leetcode.getQuestionMetadata tRPC mutation for ProjectModal fetch - Server fetchLeetCodeQuestionMetadata via LeetCode GraphQL (SSR Apollo) - Remove ApolloHydrationProvider from playground layout (tRPC-only) - Persistent AppChromeLayoutClient for profile routes (app bar scroll) - Remove per-page MainLayout from ProfilePageView Co-authored-by: maxim.kayander1 --- .../(default-locale)/(app)/profile/layout.tsx | 10 ++-- src/app/[lang]/(app)/profile/layout.tsx | 10 ++-- .../locale-app/ApolloHydrationProvider.tsx | 1 - .../playground/ui/PlaygroundLayoutClient.tsx | 13 ++--- src/features/profile/ui/ProfilePageView.tsx | 51 +++++++++--------- src/features/project/ui/ProjectModal.tsx | 28 +++++----- .../leetcode.getQuestionMetadata.test.ts | 53 +++++++++++++++++++ src/server/api/routers/leetcode.ts | 21 +++++++- .../leetcode/fetchLeetCodeQuestionMetadata.ts | 43 +++++++++++++++ 9 files changed, 173 insertions(+), 57 deletions(-) create mode 100644 src/server/api/routers/__tests__/leetcode.getQuestionMetadata.test.ts create mode 100644 src/server/leetcode/fetchLeetCodeQuestionMetadata.ts diff --git a/src/app/(default-locale)/(app)/profile/layout.tsx b/src/app/(default-locale)/(app)/profile/layout.tsx index cdff2994..c71c58a0 100644 --- a/src/app/(default-locale)/(app)/profile/layout.tsx +++ b/src/app/(default-locale)/(app)/profile/layout.tsx @@ -1,8 +1,12 @@ -/** Profile route — Apollo client is created in profile page with server prefetch. */ +import type { ReactNode } from "react"; + +import { AppChromeLayoutClient } from "#/features/appChrome/ui/AppChromeLayoutClient"; + +/** Persistent chrome for profile — Apollo prefetch stays in {@link ProfilePage}. */ export default function DefaultLocaleProfileLayout({ children, }: { - children: React.ReactNode; + children: ReactNode; }) { - return children; + return {children}; } diff --git a/src/app/[lang]/(app)/profile/layout.tsx b/src/app/[lang]/(app)/profile/layout.tsx index 6d0e3938..e7967971 100644 --- a/src/app/[lang]/(app)/profile/layout.tsx +++ b/src/app/[lang]/(app)/profile/layout.tsx @@ -1,8 +1,12 @@ -/** Profile route — Apollo client is created in profile page with server prefetch. */ +import type { ReactNode } from "react"; + +import { AppChromeLayoutClient } from "#/features/appChrome/ui/AppChromeLayoutClient"; + +/** Persistent chrome for profile — Apollo prefetch stays in {@link ProfilePage}. */ export default function LangProfileLayout({ children, }: { - children: React.ReactNode; + children: ReactNode; }) { - return children; + return {children}; } diff --git a/src/app/locale-app/ApolloHydrationProvider.tsx b/src/app/locale-app/ApolloHydrationProvider.tsx index 1c78141e..e7863030 100644 --- a/src/app/locale-app/ApolloHydrationProvider.tsx +++ b/src/app/locale-app/ApolloHydrationProvider.tsx @@ -12,7 +12,6 @@ type ApolloHydrationProviderProps = { /** * Route-scoped Apollo client with optional server-extracted cache (daily, profile). - * Playground mounts with `initialCache={null}` for {@link ProjectModal} LeetCode import only. */ export const ApolloHydrationProvider: React.FC< ApolloHydrationProviderProps diff --git a/src/features/playground/ui/PlaygroundLayoutClient.tsx b/src/features/playground/ui/PlaygroundLayoutClient.tsx index 6b1307a8..d9a4bf5e 100644 --- a/src/features/playground/ui/PlaygroundLayoutClient.tsx +++ b/src/features/playground/ui/PlaygroundLayoutClient.tsx @@ -11,7 +11,6 @@ import { PlaygroundPageShell } from "#/features/playground/ui/PlaygroundPageShel import { ProjectBrowserProvider } from "#/features/project/ui/ProjectBrowser/ProjectBrowserContext"; import { prefetchSplitPanelsLayout } from "#/shared/ui/templates/SplitPanelsLayout/prefetchSplitPanelsLayout"; -import { ApolloHydrationProvider } from "#/app/locale-app/ApolloHydrationProvider"; import { ProjectBrowserOverlay } from "#/app/locale-app/ProjectBrowserOverlay"; type PlaygroundLayoutClientProps = { @@ -40,12 +39,10 @@ export const PlaygroundLayoutClient: React.FC = ({ children, }) => { return ( - - - - {children} - - - + + + {children} + + ); }; diff --git a/src/features/profile/ui/ProfilePageView.tsx b/src/features/profile/ui/ProfilePageView.tsx index 7cb9ff6b..1d3d5320 100644 --- a/src/features/profile/ui/ProfilePageView.tsx +++ b/src/features/profile/ui/ProfilePageView.tsx @@ -18,7 +18,6 @@ import { UserSettings } from "#/features/profile/ui/UserSettings"; import { useGetUserProfileQuery } from "#/graphql/generated"; import { useI18nContext } from "#/shared/hooks"; import { useProfileUserId } from "#/shared/hooks/useProfileUserId"; -import { MainLayout } from "#/shared/ui/templates/MainLayout"; /** Profile dashboard content. Shared by Pages `/profile/[userId]` and App pilot. */ export const ProfilePageView: React.FC = () => { @@ -123,32 +122,30 @@ export const ProfilePageView: React.FC = () => { }; return ( - - - {renderAuthStatusSection()} - {session.status !== "loading" ? ( - <> - - {LL.USER_DASHBOARD({ - name: leetCodeUsername || username || "User", - })} - - - - - - - - + + {renderAuthStatusSection()} + {session.status !== "loading" ? ( + <> + + {LL.USER_DASHBOARD({ + name: leetCodeUsername || username || "User", + })} + + + + + + + - - ) : null} - - + + + ) : null} + ); }; diff --git a/src/features/project/ui/ProjectModal.tsx b/src/features/project/ui/ProjectModal.tsx index 98ee80e5..322f73c5 100644 --- a/src/features/project/ui/ProjectModal.tsx +++ b/src/features/project/ui/ProjectModal.tsx @@ -30,7 +30,6 @@ import { categoryLabels } from "#/entities/category/model/categoryLabels"; import { getDifficultyValue } from "#/entities/difficulty/lib/getDifficultyValue"; import { difficultyLabels } from "#/entities/difficulty/model/difficultyLabels"; import { projectSlice } from "#/features/project/model/projectSlice"; -import { useQuestionTitleLazyQuery } from "#/graphql/generated"; import { ProjectCategory, ProjectDifficulty, @@ -88,10 +87,6 @@ export const ProjectModal: React.FC = ({ const [problemFetchStatus, setProblemFetchStatus] = useState( ProblemFetchStatus.IDLE, ); - const [getQuestionTitle, { loading: isQuestionLoading }] = - useQuestionTitleLazyQuery({ - returnPartialData: true, - }); const prevEditMode = usePrevious(isEditMode); @@ -106,6 +101,7 @@ export const ProjectModal: React.FC = ({ const createProject = api.project.create.useMutation(); const editProject = api.project.update.useMutation(); const deleteProject = api.project.delete.useMutation(); + const fetchLeetCodeQuestion = api.leetcode.getQuestionMetadata.useMutation(); const trpcUtils = api.useUtils(); const { setProject, clearSlugs } = usePlaygroundSlugs(); @@ -250,18 +246,22 @@ export const ProjectModal: React.FC = ({ return; } - const { data } = await getQuestionTitle({ - variables: { - titleSlug, - }, + const question = await fetchLeetCodeQuestion.mutateAsync({ + titleSlug, }); - if (!data) return; + if (!question) { + setProblemFetchStatus(ProblemFetchStatus.ERROR); + enqueueSnackbar("Failed to fetch problem data!", { + variant: "error", + }); + return; + } void formik.setValues({ ...formik.values, - projectName: data.question.title, - projectSlug: data.question.titleSlug, - projectDifficulty: getDifficultyValue(data.question.difficulty), + projectName: question.title, + projectSlug: question.titleSlug, + projectDifficulty: getDifficultyValue(question.difficulty), }); setProblemFetchStatus(ProblemFetchStatus.SUCCESS); enqueueSnackbar("Problem data was successfully fetched 🎉", { @@ -440,7 +440,7 @@ export const ProjectModal: React.FC = ({ >