mirror of
https://github.com/massbug/judge4c.git
synced 2025-05-17 23:12:23 +00:00
feat(problems): add localization support for problem descriptions and solutions
- Replace cached problem data with direct Prisma queries for localized content - Implement locale-based content selection for both descriptions and solutions - Refactor skeleton loading components structure - Change all exports from named to default exports
This commit is contained in:
parent
ad2aca2f67
commit
eea16a8224
@ -1,56 +1,83 @@
|
||||
import { getCachedProblem } from "@/lib/prisma";
|
||||
import prisma from "@/lib/prisma";
|
||||
import { getLocale } from "next-intl/server";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { MdxRenderer } from "@/components/content/mdx-renderer";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
import type { Locale, ProblemLocalization } from "@/generated/client";
|
||||
|
||||
const getLocalizedDescription = (
|
||||
localizations: ProblemLocalization[],
|
||||
locale: Locale
|
||||
) => {
|
||||
if (!localizations || localizations.length === 0) {
|
||||
return "Unknown Description";
|
||||
}
|
||||
|
||||
const localization = localizations.find(
|
||||
(localization) => localization.locale === locale
|
||||
);
|
||||
|
||||
return (
|
||||
localization?.content ?? localizations[0].content ?? "Unknown Description"
|
||||
);
|
||||
};
|
||||
|
||||
interface DescriptionContentProps {
|
||||
problemId: string;
|
||||
}
|
||||
|
||||
const DescriptionContent = async ({ problemId }: DescriptionContentProps) => {
|
||||
const problem = await getCachedProblem(problemId);
|
||||
export const DescriptionContent = async ({
|
||||
problemId,
|
||||
}: DescriptionContentProps) => {
|
||||
const locale = await getLocale();
|
||||
|
||||
const descriptions = await prisma.problemLocalization.findMany({
|
||||
where: {
|
||||
problemId,
|
||||
type: "DESCRIPTION",
|
||||
},
|
||||
});
|
||||
|
||||
const description = getLocalizedDescription(descriptions, locale as Locale);
|
||||
|
||||
return (
|
||||
<ScrollArea className="h-full">
|
||||
<MdxRenderer
|
||||
source={problem?.description ?? "description not found"}
|
||||
className="p-4 md:p-6"
|
||||
/>
|
||||
<MdxRenderer source={description} className="p-4 md:p-6" />
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
);
|
||||
};
|
||||
|
||||
const DescriptionContentSkeleton = () => {
|
||||
export 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" />
|
||||
<div className="relative h-full w-full">
|
||||
<div className="absolute h-full w-full p-4 md:p-6">
|
||||
{/* Title skeleton */}
|
||||
<Skeleton className="mb-6 h-8 w-3/4" />
|
||||
|
||||
{/* 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" />
|
||||
{/* Content skeletons */}
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-5/6" />
|
||||
<Skeleton className="mb-4 h-4 w-2/3" />
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-4/5" />
|
||||
|
||||
{/* Example section heading */}
|
||||
<Skeleton className="h-6 w-1/4 mb-4 mt-8" />
|
||||
{/* Example section heading */}
|
||||
<Skeleton className="mb-4 mt-8 h-6 w-1/4" />
|
||||
|
||||
{/* Example content */}
|
||||
<Skeleton className="h-4 w-full mb-4" />
|
||||
<Skeleton className="h-4 w-5/6 mb-4" />
|
||||
{/* Example content */}
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-5/6" />
|
||||
|
||||
{/* Code block skeleton */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="h-40 w-full rounded-md" />
|
||||
{/* Code block skeleton */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="h-40 w-full rounded-md" />
|
||||
</div>
|
||||
|
||||
{/* More content */}
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-3/4" />
|
||||
</div>
|
||||
|
||||
{/* More content */}
|
||||
<Skeleton className="h-4 w-full mb-4" />
|
||||
<Skeleton className="h-4 w-3/4 mb-4" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { DescriptionContent, DescriptionContentSkeleton };
|
||||
|
@ -8,7 +8,7 @@ interface DescriptionPanelProps {
|
||||
problemId: string;
|
||||
}
|
||||
|
||||
const DescriptionPanel = ({ problemId }: DescriptionPanelProps) => {
|
||||
export const DescriptionPanel = ({ problemId }: 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">
|
||||
@ -21,5 +21,3 @@ const DescriptionPanel = ({ problemId }: DescriptionPanelProps) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { DescriptionPanel };
|
||||
|
@ -1,56 +1,81 @@
|
||||
import { getCachedProblem } from "@/lib/prisma";
|
||||
import prisma from "@/lib/prisma";
|
||||
import { getLocale } from "next-intl/server";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { MdxRenderer } from "@/components/content/mdx-renderer";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
import type { Locale, ProblemLocalization } from "@/generated/client";
|
||||
|
||||
const getLocalizedSolution = (
|
||||
localizations: ProblemLocalization[],
|
||||
locale: Locale
|
||||
) => {
|
||||
if (!localizations || localizations.length === 0) {
|
||||
return "Unknown Solution";
|
||||
}
|
||||
|
||||
const localization = localizations.find(
|
||||
(localization) => localization.locale === locale
|
||||
);
|
||||
|
||||
return (
|
||||
localization?.content ?? localizations[0].content ?? "Unknown Solution"
|
||||
);
|
||||
};
|
||||
|
||||
interface SolutionContentProps {
|
||||
problemId: string;
|
||||
}
|
||||
|
||||
const SolutionContent = async ({ problemId }: SolutionContentProps) => {
|
||||
const problem = await getCachedProblem(problemId);
|
||||
export const SolutionContent = async ({ problemId }: SolutionContentProps) => {
|
||||
const locale = await getLocale();
|
||||
|
||||
const solutions = await prisma.problemLocalization.findMany({
|
||||
where: {
|
||||
problemId,
|
||||
type: "SOLUTION",
|
||||
},
|
||||
});
|
||||
|
||||
const solution = getLocalizedSolution(solutions, locale as Locale);
|
||||
|
||||
return (
|
||||
<ScrollArea className="h-full">
|
||||
<MdxRenderer
|
||||
source={problem?.solution ?? "solution not found"}
|
||||
className="p-4 md:p-6"
|
||||
/>
|
||||
<MdxRenderer source={solution} className="p-4 md:p-6" />
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
);
|
||||
};
|
||||
|
||||
const SolutionContentSkeleton = () => {
|
||||
export 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" />
|
||||
<div className="relative h-full w-full">
|
||||
<div className="absolute h-full w-full p-4 md:p-6">
|
||||
{/* Title skeleton */}
|
||||
<Skeleton className="mb-6 h-8 w-3/4" />
|
||||
|
||||
{/* 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" />
|
||||
{/* Content skeletons */}
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-5/6" />
|
||||
<Skeleton className="mb-4 h-4 w-2/3" />
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-4/5" />
|
||||
|
||||
{/* Example section heading */}
|
||||
<Skeleton className="h-6 w-1/4 mb-4 mt-8" />
|
||||
{/* Example section heading */}
|
||||
<Skeleton className="mb-4 mt-8 h-6 w-1/4" />
|
||||
|
||||
{/* Example content */}
|
||||
<Skeleton className="h-4 w-full mb-4" />
|
||||
<Skeleton className="h-4 w-5/6 mb-4" />
|
||||
{/* Example content */}
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-5/6" />
|
||||
|
||||
{/* Code block skeleton */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="h-40 w-full rounded-md" />
|
||||
{/* Code block skeleton */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="h-40 w-full rounded-md" />
|
||||
</div>
|
||||
|
||||
{/* More content */}
|
||||
<Skeleton className="mb-4 h-4 w-full" />
|
||||
<Skeleton className="mb-4 h-4 w-3/4" />
|
||||
</div>
|
||||
|
||||
{/* More content */}
|
||||
<Skeleton className="h-4 w-full mb-4" />
|
||||
<Skeleton className="h-4 w-3/4 mb-4" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { SolutionContent, SolutionContentSkeleton };
|
||||
|
@ -8,7 +8,7 @@ interface SolutionPanelProps {
|
||||
problemId: string;
|
||||
}
|
||||
|
||||
const SolutionPanel = ({ problemId }: SolutionPanelProps) => {
|
||||
export const SolutionPanel = ({ problemId }: 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">
|
||||
@ -21,5 +21,3 @@ const SolutionPanel = ({ problemId }: SolutionPanelProps) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { SolutionPanel };
|
||||
|
Loading…
Reference in New Issue
Block a user