Initial commit: FL-Akademie LMS mit Docker, Admin, Portal und Dokumentation.
Made-with: Cursor
This commit is contained in:
153
app/admin/courses/actions.ts
Normal file
153
app/admin/courses/actions.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth-options";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { slugify } from "@/lib/slug";
|
||||
import { BillingInterval } from "@prisma/client";
|
||||
|
||||
async function assertAdmin() {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user?.id || session.user.role !== "ADMIN") {
|
||||
throw new Error("Keine Berechtigung.");
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
function parseBilling(v: FormDataEntryValue | null): BillingInterval {
|
||||
const s = String(v ?? "NONE");
|
||||
if (s === "MONTH" || s === "QUARTER" || s === "YEAR") return s;
|
||||
return BillingInterval.NONE;
|
||||
}
|
||||
|
||||
export async function createCourseAction(formData: FormData) {
|
||||
const session = await assertAdmin();
|
||||
const title = String(formData.get("title") ?? "").trim();
|
||||
if (!title) redirect("/admin/courses/new?error=title");
|
||||
|
||||
let slug = String(formData.get("slug") ?? "").trim();
|
||||
if (!slug) slug = slugify(title);
|
||||
|
||||
const exists = await prisma.course.findUnique({ where: { slug } });
|
||||
if (exists) redirect(`/admin/courses/new?error=slug`);
|
||||
|
||||
const description = String(formData.get("description") ?? "").trim();
|
||||
const authorName = String(formData.get("authorName") ?? "").trim() || session.user.name || "Admin";
|
||||
const published = String(formData.get("published") ?? "") === "on";
|
||||
const priceEuros = Number(String(formData.get("priceEuros") ?? "0").replace(",", "."));
|
||||
const priceCents = Number.isFinite(priceEuros) ? Math.max(0, Math.round(priceEuros * 100)) : 0;
|
||||
|
||||
const course = await prisma.course.create({
|
||||
data: {
|
||||
slug,
|
||||
title,
|
||||
description,
|
||||
published,
|
||||
priceCents,
|
||||
billingInterval: parseBilling(formData.get("billingInterval")),
|
||||
authorId: session.user.id,
|
||||
authorName,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/kurse");
|
||||
revalidatePath("/admin/courses");
|
||||
redirect(`/admin/courses/${course.id}/edit`);
|
||||
}
|
||||
|
||||
export async function updateCourseAction(courseId: string, formData: FormData) {
|
||||
const session = await assertAdmin();
|
||||
const title = String(formData.get("title") ?? "").trim();
|
||||
if (!title) redirect(`/admin/courses/${courseId}/edit?error=title`);
|
||||
|
||||
let slug = String(formData.get("slug") ?? "").trim();
|
||||
if (!slug) slug = slugify(title);
|
||||
|
||||
const clash = await prisma.course.findFirst({
|
||||
where: { slug, NOT: { id: courseId } },
|
||||
});
|
||||
if (clash) redirect(`/admin/courses/${courseId}/edit?error=slug`);
|
||||
|
||||
const description = String(formData.get("description") ?? "").trim();
|
||||
const authorName = String(formData.get("authorName") ?? "").trim() || session.user.name || "Admin";
|
||||
const published = String(formData.get("published") ?? "") === "on";
|
||||
const priceEuros = Number(String(formData.get("priceEuros") ?? "0").replace(",", "."));
|
||||
const priceCents = Number.isFinite(priceEuros) ? Math.max(0, Math.round(priceEuros * 100)) : 0;
|
||||
|
||||
await prisma.course.update({
|
||||
where: { id: courseId },
|
||||
data: {
|
||||
slug,
|
||||
title,
|
||||
description,
|
||||
published,
|
||||
priceCents,
|
||||
billingInterval: parseBilling(formData.get("billingInterval")),
|
||||
authorName,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/kurse");
|
||||
revalidatePath("/admin/courses");
|
||||
revalidatePath(`/kurse/${slug}`);
|
||||
redirect(`/admin/courses/${courseId}/edit?saved=1`);
|
||||
}
|
||||
|
||||
export async function createModuleAction(courseId: string, formData: FormData) {
|
||||
await assertAdmin();
|
||||
const title = String(formData.get("title") ?? "").trim();
|
||||
if (!title) redirect(`/admin/courses/${courseId}/edit?error=module`);
|
||||
|
||||
const max = await prisma.courseModule.aggregate({
|
||||
where: { courseId },
|
||||
_max: { sortOrder: true },
|
||||
});
|
||||
const sortOrder = (max._max.sortOrder ?? -1) + 1;
|
||||
|
||||
await prisma.courseModule.create({
|
||||
data: { courseId, title, sortOrder },
|
||||
});
|
||||
|
||||
revalidatePath(`/admin/courses/${courseId}/edit`);
|
||||
redirect(`/admin/courses/${courseId}/edit`);
|
||||
}
|
||||
|
||||
export async function createLessonAction(courseId: string, formData: FormData) {
|
||||
await assertAdmin();
|
||||
const moduleId = String(formData.get("moduleId") ?? "").trim();
|
||||
const title = String(formData.get("lessonTitle") ?? "").trim();
|
||||
if (!moduleId || !title) redirect(`/admin/courses/${courseId}/edit?error=lesson`);
|
||||
|
||||
const mod = await prisma.courseModule.findFirst({
|
||||
where: { id: moduleId, courseId },
|
||||
});
|
||||
if (!mod) redirect(`/admin/courses/${courseId}/edit?error=lesson`);
|
||||
|
||||
let slug = String(formData.get("lessonSlug") ?? "").trim();
|
||||
if (!slug) slug = slugify(title);
|
||||
|
||||
const contentHtml = String(formData.get("contentHtml") ?? "").trim();
|
||||
|
||||
const max = await prisma.lesson.aggregate({
|
||||
where: { moduleId },
|
||||
_max: { sortOrder: true },
|
||||
});
|
||||
const sortOrder = (max._max.sortOrder ?? -1) + 1;
|
||||
|
||||
const existing = await prisma.lesson.findUnique({
|
||||
where: { moduleId_slug: { moduleId, slug } },
|
||||
});
|
||||
if (existing) redirect(`/admin/courses/${courseId}/edit?error=lessonSlug`);
|
||||
|
||||
await prisma.lesson.create({
|
||||
data: { moduleId, slug, title, contentHtml, sortOrder, published: true },
|
||||
});
|
||||
|
||||
const course = await prisma.course.findUnique({ where: { id: courseId }, select: { slug: true } });
|
||||
if (course?.slug) revalidatePath(`/kurse/${course.slug}`);
|
||||
|
||||
revalidatePath(`/admin/courses/${courseId}/edit`);
|
||||
redirect(`/admin/courses/${courseId}/edit`);
|
||||
}
|
||||
Reference in New Issue
Block a user