Pending course, rest ready for launch
This commit is contained in:
@@ -5,6 +5,11 @@ import { requireTeacher } from "@/lib/auth/requireTeacher";
|
||||
import { z } from "zod";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { ContentStatus, Prisma, ProficiencyLevel } from "@prisma/client";
|
||||
import {
|
||||
buildLessonDescriptionMeta,
|
||||
parseLessonDescriptionMeta,
|
||||
type LessonContentType,
|
||||
} from "@/lib/courses/lessonContent";
|
||||
|
||||
// --- VALIDATION SCHEMAS (Zod) ---
|
||||
|
||||
@@ -129,6 +134,17 @@ export async function deleteCourse(courseId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function parseLearningOutcomes(raw: FormDataEntryValue | null): string[] {
|
||||
if (raw == null || typeof raw !== "string") return [];
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed.filter((x): x is string => typeof x === "string");
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateCourse(courseId: string, courseSlug: string, formData: FormData) {
|
||||
const user = await requireTeacher();
|
||||
if (!user) return { success: false, error: "Unauthorized" };
|
||||
@@ -140,6 +156,7 @@ export async function updateCourse(courseId: string, courseSlug: string, formDat
|
||||
const level = formData.get("level") as ProficiencyLevel;
|
||||
const status = formData.get("status") as ContentStatus;
|
||||
const price = parseFloat(formData.get("price") as string) || 0;
|
||||
const learningOutcomes = parseLearningOutcomes(formData.get("learningOutcomes"));
|
||||
|
||||
await db.course.update({
|
||||
where: { id: courseId, authorId: user.id },
|
||||
@@ -149,12 +166,16 @@ export async function updateCourse(courseId: string, courseSlug: string, formDat
|
||||
level,
|
||||
status,
|
||||
price,
|
||||
},
|
||||
learningOutcomes:
|
||||
learningOutcomes.length > 0 ? (learningOutcomes as Prisma.InputJsonValue) : Prisma.JsonNull,
|
||||
} as Prisma.CourseUpdateInput,
|
||||
});
|
||||
|
||||
// Revalidate both the list and the editor (edit route uses slug, not id)
|
||||
// Revalidate teacher list, editor page + layout (so router.refresh() gets fresh data), and public catalog
|
||||
revalidatePath("/teacher/courses");
|
||||
revalidatePath(`/teacher/courses/${courseSlug}/edit`, "page");
|
||||
revalidatePath(`/teacher/courses/${courseSlug}/edit`, "layout");
|
||||
revalidatePath("/courses");
|
||||
return { success: true };
|
||||
} catch {
|
||||
return { success: false, error: "Failed to update" };
|
||||
@@ -163,8 +184,13 @@ export async function updateCourse(courseId: string, courseSlug: string, formDat
|
||||
|
||||
export async function updateLesson(lessonId: string, data: {
|
||||
title?: string;
|
||||
description?: Prisma.InputJsonValue | null;
|
||||
description?: string;
|
||||
videoUrl?: string;
|
||||
youtubeUrl?: string;
|
||||
materialUrl?: string;
|
||||
contentType?: LessonContentType;
|
||||
estimatedDurationMinutes?: number;
|
||||
isPreview?: boolean; // maps to DB field isFreePreview
|
||||
isPublished?: boolean; // optional: for later
|
||||
}) {
|
||||
const user = await requireTeacher();
|
||||
@@ -173,8 +199,30 @@ export async function updateLesson(lessonId: string, data: {
|
||||
try {
|
||||
const updateData: Prisma.LessonUpdateInput = { updatedAt: new Date() };
|
||||
if (data.title !== undefined) updateData.title = data.title;
|
||||
if (data.description !== undefined) updateData.description = data.description === null ? Prisma.JsonNull : data.description;
|
||||
if (data.videoUrl !== undefined) updateData.videoUrl = data.videoUrl;
|
||||
if (data.youtubeUrl !== undefined) updateData.youtubeUrl = data.youtubeUrl;
|
||||
if (data.estimatedDurationMinutes !== undefined) {
|
||||
const minutes = Math.max(0, Math.round(data.estimatedDurationMinutes));
|
||||
updateData.estimatedDuration = minutes * 60;
|
||||
}
|
||||
if (data.isPreview !== undefined) updateData.isFreePreview = data.isPreview;
|
||||
|
||||
const shouldUpdateMeta =
|
||||
data.description !== undefined || data.contentType !== undefined || data.materialUrl !== undefined;
|
||||
|
||||
if (shouldUpdateMeta) {
|
||||
const lesson = await db.lesson.findUnique({
|
||||
where: { id: lessonId },
|
||||
select: { description: true },
|
||||
});
|
||||
|
||||
const existingMeta = parseLessonDescriptionMeta(lesson?.description);
|
||||
updateData.description = buildLessonDescriptionMeta({
|
||||
text: data.description ?? existingMeta.text,
|
||||
contentType: data.contentType ?? existingMeta.contentType,
|
||||
materialUrl: data.materialUrl ?? existingMeta.materialUrl,
|
||||
});
|
||||
}
|
||||
|
||||
await db.lesson.update({
|
||||
where: { id: lessonId },
|
||||
@@ -211,7 +259,7 @@ export async function createModule(courseId: string) {
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(`/teacher/courses/${courseId}/edit`, "page");
|
||||
revalidatePath("/teacher/courses");
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Create Module Error:", error);
|
||||
@@ -219,6 +267,38 @@ export async function createModule(courseId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UPDATE MODULE TITLE
|
||||
*/
|
||||
export async function updateModuleTitle(moduleId: string, title: string) {
|
||||
const user = await requireTeacher();
|
||||
if (!user) return { success: false, error: "Unauthorized" };
|
||||
|
||||
const trimmed = title?.trim() || "";
|
||||
if (!trimmed) return { success: false, error: "El título no puede estar vacío" };
|
||||
|
||||
try {
|
||||
const moduleRow = await db.module.findFirst({
|
||||
where: { id: moduleId, course: { authorId: user.id } },
|
||||
select: { id: true, course: { select: { slug: true } } },
|
||||
});
|
||||
if (!moduleRow) return { success: false, error: "Módulo no encontrado" };
|
||||
|
||||
await db.module.update({
|
||||
where: { id: moduleId },
|
||||
data: { title: trimmed },
|
||||
});
|
||||
|
||||
revalidatePath("/teacher/courses");
|
||||
revalidatePath(`/teacher/courses/${moduleRow.course.slug}/edit`, "page");
|
||||
revalidatePath(`/teacher/courses/${moduleRow.course.slug}/edit`, "layout");
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Update Module Title Error:", error);
|
||||
return { success: false, error: "No se pudo actualizar el título" };
|
||||
}
|
||||
}
|
||||
|
||||
// 2. CREATE LESSON
|
||||
export async function createLesson(moduleId: string) {
|
||||
const user = await requireTeacher();
|
||||
@@ -238,6 +318,9 @@ export async function createLesson(moduleId: string) {
|
||||
data: {
|
||||
moduleId,
|
||||
title: "Nueva Lección",
|
||||
description: {
|
||||
contentType: "VIDEO",
|
||||
},
|
||||
orderIndex: newOrder,
|
||||
estimatedDuration: 0,
|
||||
version: 1,
|
||||
@@ -383,4 +466,4 @@ export async function reorderLessons(lessonId: string, direction: "up" | "down")
|
||||
|
||||
revalidatePath(`/teacher/courses/${currentLesson.module.course.slug}/edit`, "page");
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user