Serialize all Prisma data before passing to Client Components

Prisma 7 proxy objects (DateTime, _count, relations) cannot be
serialized by React RSC. Convert all course page data to plain
JSON objects with JSON.parse/stringify before passing as props.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 13:42:05 +05:00
parent d8be6d6d95
commit 0bde11b86e
+17 -9
View File
@@ -50,13 +50,21 @@ export default async function CourseDetailPage({ params }: Props) {
if (!course) notFound(); if (!course) notFound();
// Prisma 7 returns proxy objects for relations/aggregates that RSC cannot serialize.
// Convert to plain JS before passing to Client Components.
const plain = JSON.parse(JSON.stringify({ course, allStudents, categories })) as {
course: typeof course;
allStudents: typeof allStudents;
categories: typeof categories;
};
return ( return (
<div className="p-8 max-w-4xl"> <div className="p-8 max-w-4xl">
{/* Breadcrumb */} {/* Breadcrumb */}
<nav className="text-xs mb-6 uppercase tracking-widest" style={{ color: "var(--muted-foreground)" }}> <nav className="text-xs mb-6 uppercase tracking-widest" style={{ color: "var(--muted-foreground)" }}>
<Link href="/admin/courses" className="hover:underline">Курсы</Link> <Link href="/admin/courses" className="hover:underline">Курсы</Link>
<span className="mx-2">/</span> <span className="mx-2">/</span>
<span style={{ color: "var(--foreground)" }}>{course.title}</span> <span style={{ color: "var(--foreground)" }}>{plain.course.title}</span>
</nav> </nav>
{/* Course metadata */} {/* Course metadata */}
@@ -64,7 +72,7 @@ export default async function CourseDetailPage({ params }: Props) {
<p className="text-xs font-bold uppercase tracking-widest mb-5" style={{ color: "var(--muted-foreground)" }}> <p className="text-xs font-bold uppercase tracking-widest mb-5" style={{ color: "var(--muted-foreground)" }}>
Основная информация Основная информация
</p> </p>
<CourseEditForm course={course} categories={categories} /> <CourseEditForm course={plain.course} categories={plain.categories} />
</section> </section>
{/* Modules */} {/* Modules */}
@@ -74,19 +82,19 @@ export default async function CourseDetailPage({ params }: Props) {
Модули Модули
</p> </p>
<span className="text-xs" style={{ color: "var(--muted-foreground)" }}> <span className="text-xs" style={{ color: "var(--muted-foreground)" }}>
{course.modules.length} модулей {plain.course.modules.length} модулей
</span> </span>
</div> </div>
<SortableModules courseId={courseId} modules={course.modules} /> <SortableModules courseId={courseId} modules={plain.course.modules} />
</section> </section>
{/* Course tree overview */} {/* Course tree overview */}
{course.modules.length > 0 && ( {plain.course.modules.length > 0 && (
<section className="card-aubade p-6 mb-6"> <section className="card-aubade p-6 mb-6">
<p className="text-xs font-bold uppercase tracking-widest mb-5" style={{ color: "var(--muted-foreground)" }}> <p className="text-xs font-bold uppercase tracking-widest mb-5" style={{ color: "var(--muted-foreground)" }}>
Структура курса Структура курса
</p> </p>
<CourseTree courseId={courseId} modules={course.modules} /> <CourseTree courseId={courseId} modules={plain.course.modules} />
</section> </section>
)} )}
@@ -97,9 +105,9 @@ export default async function CourseDetailPage({ params }: Props) {
</p> </p>
<EnrollmentManager <EnrollmentManager
courseId={courseId} courseId={courseId}
allStudents={allStudents} allStudents={plain.allStudents}
enrollments={course.enrollments} enrollments={plain.course.enrollments}
accessLogs={course.accessLogs} accessLogs={plain.course.accessLogs}
/> />
</section> </section>
</div> </div>