refactor(problems): migrate description and solution to feature-based structure

- Remove old parallel route implementations (@Description and @Solutions)
- Add new feature-based components for problem description and solution
  - Create content and panel components for both features
  - Implement skeleton loading states
  - Use cached data fetching
- Update MDX rendering and scroll area implementations
This commit is contained in:
cfngc4594 2025-05-06 19:38:50 +08:00
parent f464fb7636
commit 5f3eb72d00
8 changed files with 162 additions and 117 deletions

View File

@ -1,16 +0,0 @@
import { Suspense } from "react";
import { Loading } from "@/components/loading";
interface DescriptionLayoutProps {
children: React.ReactNode;
}
export default function DescriptionLayout({ children }: DescriptionLayoutProps) {
return (
<div className="h-full flex flex-col border border-t-0 border-muted rounded-b-3xl bg-background">
<Suspense fallback={<Loading />}>
{children}
</Suspense>
</div>
);
}

View File

@ -1,42 +0,0 @@
import prisma from "@/lib/prisma";
import { notFound } from "next/navigation";
import MdxPreview from "@/components/mdx-preview";
import { ScrollArea } from "@/components/ui/scroll-area";
import ProblemDescriptionFooter from "@/components/features/playground/problem/description/footer";
interface DescriptionPageProps {
params: Promise<{ id: string }>;
}
export default async function DescriptionPage({ params }: DescriptionPageProps) {
const { id } = await params;
if (!id) {
return notFound();
}
const problem = await prisma.problem.findUnique({
where: { id },
select: {
title: true,
description: true,
},
});
if (!problem) {
return notFound();
}
return (
<>
<div className="relative flex-1">
<div className="absolute h-full w-full">
<ScrollArea className="h-full [&>[data-radix-scroll-area-viewport]>div:min-w-0 [&>[data-radix-scroll-area-viewport]>div]:!block bg-background">
<MdxPreview source={problem.description} className="p-4 md:p-6" />
</ScrollArea>
</div>
</div>
<ProblemDescriptionFooter title={problem.title} />
</>
);
}

View File

@ -1,16 +0,0 @@
import { Suspense } from "react";
import { Loading } from "@/components/loading";
interface SolutionsLayoutProps {
children: React.ReactNode;
}
export default async function SolutionsLayout({ children }: SolutionsLayoutProps) {
return (
<div className="flex flex-col h-full border border-t-0 border-muted rounded-b-3xl bg-background">
<Suspense fallback={<Loading />}>
{children}
</Suspense>
</div>
);
}

View File

@ -1,43 +0,0 @@
import prisma from "@/lib/prisma";
import { notFound } from "next/navigation";
import MdxPreview from "@/components/mdx-preview";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import ProblemSolutionFooter from "@/components/features/playground/problem/solution/footer";
interface SolutionsPageProps {
params: Promise<{ id: string }>;
}
export default async function SolutionsPage({ params }: SolutionsPageProps) {
const { id } = await params;
if (!id) {
return notFound();
}
const problem = await prisma.problem.findUnique({
where: { id },
select: {
title: true,
solution: true,
},
});
if (!problem) {
return notFound();
}
return (
<>
<div className="relative flex-1">
<div className="absolute h-full w-full">
<ScrollArea className="h-full [&>[data-radix-scroll-area-viewport]>div:min-w-0 [&>[data-radix-scroll-area-viewport]>div]:!block bg-background">
<MdxPreview source={problem.solution} className="p-4 md:p-6" />
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
</div>
<ProblemSolutionFooter title={problem.title} />
</>
);
}

View File

@ -0,0 +1,56 @@
import { getCachedProblem } from "@/lib/prisma";
import { Skeleton } from "@/components/ui/skeleton";
import { MdxRenderer } from "@/components/content/mdx-renderer";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
interface DescriptionContentProps {
id: string;
}
const DescriptionContent = async ({ id }: DescriptionContentProps) => {
const problem = await getCachedProblem(id);
return (
<ScrollArea className="h-full">
<MdxRenderer
source={problem?.description ?? "description not found"}
className="p-4 md:p-6"
/>
<ScrollBar orientation="horizontal" />
</ScrollArea>
);
};
const DescriptionContentSkeleton = () => {
return (
<div className="h-full flex flex-col p-4 md:p-6">
{/* Title skeleton */}
<Skeleton className="h-8 w-3/4 mb-6" />
{/* Content skeletons */}
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-5/6 mb-4" />
<Skeleton className="h-4 w-2/3 mb-4" />
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-4/5 mb-4" />
{/* Example section heading */}
<Skeleton className="h-6 w-1/4 mb-4 mt-8" />
{/* Example content */}
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-5/6 mb-4" />
{/* Code block skeleton */}
<div className="mb-6">
<Skeleton className="h-40 w-full rounded-md" />
</div>
{/* More content */}
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-3/4 mb-4" />
</div>
);
};
export { DescriptionContent, DescriptionContentSkeleton };

View File

@ -0,0 +1,25 @@
import { Suspense } from "react";
import {
DescriptionContent,
DescriptionContentSkeleton,
} from "@/features/problems/description/components/content";
interface DescriptionPanelProps {
id: string;
}
const DescriptionPanel = ({ id }: DescriptionPanelProps) => {
return (
<div className="h-full flex flex-col border border-t-0 border-muted rounded-b-3xl bg-background overflow-hidden">
<div className="relative flex-1">
<div className="absolute h-full w-full">
<Suspense fallback={<DescriptionContentSkeleton />}>
<DescriptionContent id={id} />
</Suspense>
</div>
</div>
</div>
);
};
export { DescriptionPanel };

View File

@ -0,0 +1,56 @@
import { getCachedProblem } from "@/lib/prisma";
import { Skeleton } from "@/components/ui/skeleton";
import { MdxRenderer } from "@/components/content/mdx-renderer";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
interface SolutionContentProps {
id: string;
}
const SolutionContent = async ({ id }: SolutionContentProps) => {
const problem = await getCachedProblem(id);
return (
<ScrollArea className="h-full">
<MdxRenderer
source={problem?.solution ?? "solution not found"}
className="p-4 md:p-6"
/>
<ScrollBar orientation="horizontal" />
</ScrollArea>
);
};
const SolutionContentSkeleton = () => {
return (
<div className="h-full flex flex-col p-4 md:p-6">
{/* Title skeleton */}
<Skeleton className="h-8 w-3/4 mb-6" />
{/* Content skeletons */}
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-5/6 mb-4" />
<Skeleton className="h-4 w-2/3 mb-4" />
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-4/5 mb-4" />
{/* Example section heading */}
<Skeleton className="h-6 w-1/4 mb-4 mt-8" />
{/* Example content */}
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-5/6 mb-4" />
{/* Code block skeleton */}
<div className="mb-6">
<Skeleton className="h-40 w-full rounded-md" />
</div>
{/* More content */}
<Skeleton className="h-4 w-full mb-4" />
<Skeleton className="h-4 w-3/4 mb-4" />
</div>
);
};
export { SolutionContent, SolutionContentSkeleton };

View File

@ -0,0 +1,25 @@
import { Suspense } from "react";
import {
SolutionContent,
SolutionContentSkeleton,
} from "@/features/problems/solution/components/content";
interface SolutionPanelProps {
id: string;
}
const SolutionPanel = ({ id }: SolutionPanelProps) => {
return (
<div className="h-full flex flex-col border border-t-0 border-muted rounded-b-3xl bg-background overflow-hidden">
<div className="relative flex-1">
<div className="absolute h-full w-full">
<Suspense fallback={<SolutionContentSkeleton />}>
<SolutionContent id={id} />
</Suspense>
</div>
</div>
</div>
);
};
export { SolutionPanel };