First commit
This commit is contained in:
133
lib/data/teacherCourses.ts
Normal file
133
lib/data/teacherCourses.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import type { Course, CourseLevel, Lesson } from "@/types/course";
|
||||
|
||||
const STORAGE_KEY = "acve.teacher-courses.v1";
|
||||
const TEACHER_UPDATED_EVENT = "acve:teacher-courses-updated";
|
||||
|
||||
export type TeacherCourseInput = {
|
||||
title: string;
|
||||
level: CourseLevel;
|
||||
summary: string;
|
||||
instructor: string;
|
||||
weeks: number;
|
||||
};
|
||||
|
||||
export type TeacherCoursePatch = Partial<Omit<Course, "slug" | "lessons">> & {
|
||||
summary?: string;
|
||||
title?: string;
|
||||
level?: CourseLevel;
|
||||
instructor?: string;
|
||||
weeks?: number;
|
||||
};
|
||||
|
||||
const isBrowser = () => typeof window !== "undefined";
|
||||
|
||||
const readStore = (): Course[] => {
|
||||
if (!isBrowser()) return [];
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) return [];
|
||||
|
||||
try {
|
||||
return JSON.parse(raw) as Course[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const writeStore = (courses: Course[]) => {
|
||||
if (!isBrowser()) return;
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(courses));
|
||||
window.dispatchEvent(new Event(TEACHER_UPDATED_EVENT));
|
||||
};
|
||||
|
||||
const slugify = (value: string) =>
|
||||
value
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9\s-]/g, "")
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/-+/g, "-");
|
||||
|
||||
const lessonId = (index: number) => `lesson-${index}`;
|
||||
|
||||
const normalizeCourse = (course: Course): Course => ({
|
||||
...course,
|
||||
lessonsCount: course.lessons.length,
|
||||
});
|
||||
|
||||
export const teacherCoursesUpdatedEventName = TEACHER_UPDATED_EVENT;
|
||||
|
||||
export const getTeacherCourses = (): Course[] => readStore().map(normalizeCourse);
|
||||
|
||||
export const getTeacherCourseBySlug = (slug: string): Course | undefined =>
|
||||
getTeacherCourses().find((course) => course.slug === slug);
|
||||
|
||||
export const ensureUniqueTeacherCourseSlug = (title: string, reservedSlugs: string[]) => {
|
||||
const baseSlug = slugify(title) || "untitled-course";
|
||||
let slug = baseSlug;
|
||||
let index = 2;
|
||||
|
||||
const pool = new Set(reservedSlugs.map((item) => item.toLowerCase()));
|
||||
while (pool.has(slug.toLowerCase())) {
|
||||
slug = `${baseSlug}-${index}`;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return slug;
|
||||
};
|
||||
|
||||
export const createTeacherCourse = (input: TeacherCourseInput, reservedSlugs: string[]) => {
|
||||
const courses = getTeacherCourses();
|
||||
const slug = ensureUniqueTeacherCourseSlug(input.title, reservedSlugs);
|
||||
|
||||
const starterLesson: Lesson = {
|
||||
id: lessonId(1),
|
||||
title: "Course introduction",
|
||||
type: "video",
|
||||
minutes: 8,
|
||||
isPreview: true,
|
||||
videoUrl: "https://example.com/video-placeholder",
|
||||
};
|
||||
|
||||
const next: Course = {
|
||||
slug,
|
||||
title: input.title,
|
||||
level: input.level,
|
||||
summary: input.summary,
|
||||
rating: 5,
|
||||
weeks: Math.max(1, input.weeks),
|
||||
lessonsCount: 1,
|
||||
students: 0,
|
||||
instructor: input.instructor,
|
||||
lessons: [starterLesson],
|
||||
};
|
||||
|
||||
writeStore([...courses, next]);
|
||||
return next;
|
||||
};
|
||||
|
||||
export const updateTeacherCourse = (slug: string, patch: TeacherCoursePatch) => {
|
||||
const courses = getTeacherCourses();
|
||||
const nextCourses = courses.map((course) =>
|
||||
course.slug === slug ? normalizeCourse({ ...course, ...patch }) : course,
|
||||
);
|
||||
writeStore(nextCourses);
|
||||
return nextCourses.find((course) => course.slug === slug);
|
||||
};
|
||||
|
||||
export const addLessonToTeacherCourse = (slug: string, lesson: Omit<Lesson, "id">) => {
|
||||
const courses = getTeacherCourses();
|
||||
const nextCourses = courses.map((course) => {
|
||||
if (course.slug !== slug) return course;
|
||||
const nextLesson: Lesson = {
|
||||
...lesson,
|
||||
id: lessonId(course.lessons.length + 1),
|
||||
};
|
||||
return normalizeCourse({
|
||||
...course,
|
||||
lessons: [...course.lessons, nextLesson],
|
||||
});
|
||||
});
|
||||
|
||||
writeStore(nextCourses);
|
||||
return nextCourses.find((course) => course.slug === slug);
|
||||
};
|
||||
Reference in New Issue
Block a user