mirror of
				https://github.com/massbug/judge4c.git
				synced 2025-11-03 18:03:35 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									f464fb7636
								
							
						
					
					
						commit
						5f3eb72d00
					
				@ -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>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -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} />
 | 
					 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -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>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -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} />
 | 
					 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										56
									
								
								src/features/problems/description/components/content.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/features/problems/description/components/content.tsx
									
									
									
									
									
										Normal 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 };
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/features/problems/description/components/panel.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/features/problems/description/components/panel.tsx
									
									
									
									
									
										Normal 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 };
 | 
				
			||||||
							
								
								
									
										56
									
								
								src/features/problems/solution/components/content.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/features/problems/solution/components/content.tsx
									
									
									
									
									
										Normal 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 };
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/features/problems/solution/components/panel.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/features/problems/solution/components/panel.tsx
									
									
									
									
									
										Normal 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 };
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user