mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
wip: course layout and func to context
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
import { createContext, useCallback, useContext, useState } from "react";
|
||||
import api from "@/utils/api.util";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const CoursesContext = createContext(null);
|
||||
|
||||
export function useCourses() {
|
||||
const ctx = useContext(CoursesContext);
|
||||
if (!ctx) throw new Error("useCourses must be used within a CoursesProvider");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// ─── Initial States ───────────────────────────────────────────────────────────
|
||||
|
||||
const PAGINATION_INIT = {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
totalRecords: 0,
|
||||
totalPages: 0,
|
||||
hasPrevPage: false,
|
||||
hasNextPage: false,
|
||||
};
|
||||
|
||||
const BASE = "/admin/courses";
|
||||
|
||||
export function CoursesProvider({ children }) {
|
||||
|
||||
// ─── State ────────────────────────────────────────────────────────────────
|
||||
const [courses, setCourses] = useState([]);
|
||||
const [course, setCourse] = useState(null);
|
||||
const [units, setUnits] = useState([]);
|
||||
const [unit, setUnit] = useState(null);
|
||||
const [lessons, setLessons] = useState([]);
|
||||
const [lesson, setLesson] = useState(null);
|
||||
const [lessonPage, setLessonPage] = useState(null);
|
||||
const [attributes, setAttributes] = useState([]);
|
||||
const [pagination, setPagination] = useState(PAGINATION_INIT);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// ─── Request wrapper ──────────────────────────────────────────────────────
|
||||
const request = useCallback(async (fn) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
return await fn();
|
||||
} catch (err) {
|
||||
const message = err?.response?.data?.message ?? "Something went wrong.";
|
||||
toast.error(message);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// =========================================================================
|
||||
// ─── COURSES ─────────────────────────────────────────────────────────────
|
||||
// =========================================================================
|
||||
|
||||
// ─── GET /admin/courses ───────────────────────────────────────────────────
|
||||
const fetchCourses = useCallback(
|
||||
(params = {}) =>
|
||||
request(async () => {
|
||||
const { filters, sort, ...rest } = params;
|
||||
const { data } = await api.get(BASE, {
|
||||
params: {
|
||||
...rest,
|
||||
...(filters?.length ? { filters: JSON.stringify(filters) } : {}),
|
||||
...(sort?.length ? { sort: JSON.stringify(sort) } : {}),
|
||||
},
|
||||
});
|
||||
const final_data = data?.data;
|
||||
setCourses(final_data?.data ?? []);
|
||||
setPagination(final_data?.pagination ?? PAGINATION_INIT);
|
||||
if (final_data?.attributes?.length) setAttributes(final_data.attributes);
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── GET /admin/courses/:courseId ─────────────────────────────────────────
|
||||
const fetchCourse = useCallback(
|
||||
(courseId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}`);
|
||||
const result = data?.data?.data ?? null;
|
||||
setCourse(result);
|
||||
setUnits(result?.units ?? []);
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── POST /admin/courses ──────────────────────────────────────────────────
|
||||
const createCourse = useCallback(
|
||||
(payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.post(BASE, payload);
|
||||
const course = data?.data?.data ?? null;
|
||||
if (course) {
|
||||
setCourses((prev) => [course, ...prev]);
|
||||
toast.success("Course created successfully.");
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── PATCH /admin/courses/:courseId ───────────────────────────────────────
|
||||
const updateCourse = useCallback(
|
||||
(courseId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}`, payload);
|
||||
const course = data?.data?.data ?? null;
|
||||
if (course) {
|
||||
setCourses((prev) => prev.map((c) => (c.course_id === courseId ? course : c)));
|
||||
setCourse(course);
|
||||
toast.success("Course updated successfully.");
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── DELETE /admin/courses/:courseId ──────────────────────────────────────
|
||||
const deleteCourse = useCallback(
|
||||
(courseId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}`);
|
||||
setCourses((prev) => prev.filter((c) => c.course_id !== courseId));
|
||||
setCourse((prev) => (prev?.course_id === courseId ? null : prev));
|
||||
toast.success("Course deleted.");
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// =========================================================================
|
||||
// ─── UNITS ───────────────────────────────────────────────────────────────
|
||||
// =========================================================================
|
||||
|
||||
// ─── GET /admin/courses/:courseId/units ───────────────────────────────────
|
||||
const fetchUnits = useCallback(
|
||||
(courseId, params = {}) =>
|
||||
request(async () => {
|
||||
const { filters, sort, ...rest } = params;
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units`, {
|
||||
params: {
|
||||
...rest,
|
||||
...(filters?.length ? { filters: JSON.stringify(filters) } : {}),
|
||||
...(sort?.length ? { sort: JSON.stringify(sort) } : {}),
|
||||
},
|
||||
});
|
||||
const final_data = data?.data;
|
||||
setUnits(final_data?.data ?? []);
|
||||
setPagination(final_data?.pagination ?? PAGINATION_INIT);
|
||||
if (final_data?.attributes?.length) setAttributes(final_data.attributes);
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── GET /admin/courses/:courseId/units/:unitId ───────────────────────────
|
||||
const fetchUnit = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}`);
|
||||
const result = data?.data?.data ?? null;
|
||||
setUnit(result);
|
||||
setLessons(result?.lessons ?? []);
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── POST /admin/courses/:courseId/units ──────────────────────────────────
|
||||
const createUnit = useCallback(
|
||||
(courseId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.post(`${BASE}/${courseId}/units`, payload);
|
||||
const unit = data?.data?.data ?? null;
|
||||
if (unit) {
|
||||
setUnits((prev) => [...prev, unit]);
|
||||
toast.success("Unit created successfully.");
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── PATCH /admin/courses/:courseId/units/:unitId ─────────────────────────
|
||||
const updateUnit = useCallback(
|
||||
(courseId, unitId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}`, payload);
|
||||
const unit = data?.data?.data ?? null;
|
||||
if (unit) {
|
||||
setUnits((prev) => prev.map((u) => (u.unit_id === unitId ? unit : u)));
|
||||
setUnit(unit);
|
||||
toast.success("Unit updated successfully.");
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── DELETE /admin/courses/:courseId/units/:unitId ────────────────────────
|
||||
const deleteUnit = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}`);
|
||||
setUnits((prev) => prev.filter((u) => u.unit_id !== unitId));
|
||||
setUnit((prev) => (prev?.unit_id === unitId ? null : prev));
|
||||
toast.success("Unit deleted.");
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// =========================================================================
|
||||
// ─── LESSONS ─────────────────────────────────────────────────────────────
|
||||
// =========================================================================
|
||||
|
||||
// ─── GET /admin/courses/:courseId/units/:unitId/lessons ───────────────────
|
||||
const fetchLessons = useCallback(
|
||||
(courseId, unitId, params = {}) =>
|
||||
request(async () => {
|
||||
const { filters, sort, ...rest } = params;
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/lessons`, {
|
||||
params: {
|
||||
...rest,
|
||||
...(filters?.length ? { filters: JSON.stringify(filters) } : {}),
|
||||
...(sort?.length ? { sort: JSON.stringify(sort) } : {}),
|
||||
},
|
||||
});
|
||||
const final_data = data?.data;
|
||||
setLessons(final_data?.data ?? []);
|
||||
setPagination(final_data?.pagination ?? PAGINATION_INIT);
|
||||
if (final_data?.attributes?.length) setAttributes(final_data.attributes);
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── GET /admin/courses/:courseId/units/:unitId/lessons/:lessonId ─────────
|
||||
const fetchLesson = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(
|
||||
`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}`
|
||||
);
|
||||
const result = data?.data?.data ?? null;
|
||||
setLesson(result);
|
||||
setLessonPage(result?.page ?? null);
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── POST /admin/courses/:courseId/units/:unitId/lessons ──────────────────
|
||||
const createLesson = useCallback(
|
||||
(courseId, unitId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.post(
|
||||
`${BASE}/${courseId}/units/${unitId}/lessons`,
|
||||
payload
|
||||
);
|
||||
const lesson = data?.data?.data ?? null;
|
||||
if (lesson) {
|
||||
setLessons((prev) => [...prev, lesson]);
|
||||
toast.success("Lesson created successfully.");
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── PATCH /admin/courses/:courseId/units/:unitId/lessons/:lessonId ───────
|
||||
const updateLesson = useCallback(
|
||||
(courseId, unitId, lessonId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(
|
||||
`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}`,
|
||||
payload
|
||||
);
|
||||
const lesson = data?.data?.data ?? null;
|
||||
if (lesson) {
|
||||
setLessons((prev) => prev.map((l) => (l.lesson_id === lessonId ? lesson : l)));
|
||||
setLesson(lesson);
|
||||
toast.success("Lesson updated successfully.");
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── DELETE /admin/courses/:courseId/units/:unitId/lessons/:lessonId ──────
|
||||
const deleteLesson = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(
|
||||
`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}`
|
||||
);
|
||||
setLessons((prev) => prev.filter((l) => l.lesson_id !== lessonId));
|
||||
setLesson((prev) => (prev?.lesson_id === lessonId ? null : prev));
|
||||
toast.success("Lesson deleted.");
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// =========================================================================
|
||||
// ─── LESSON PAGE ─────────────────────────────────────────────────────────
|
||||
// =========================================================================
|
||||
|
||||
// ─── GET .../lessons/:lessonId/page ───────────────────────────────────────
|
||||
const fetchLessonPage = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(
|
||||
`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}/page`
|
||||
);
|
||||
const page = data?.data?.data ?? null;
|
||||
setLessonPage(page);
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── PUT .../lessons/:lessonId/page ───────────────────────────────────────
|
||||
const saveLessonPage = useCallback(
|
||||
(courseId, unitId, lessonId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.put(
|
||||
`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}/page`,
|
||||
payload
|
||||
);
|
||||
const page = data?.data?.data ?? null;
|
||||
if (page) {
|
||||
setLessonPage(page);
|
||||
toast.success("Lesson page saved.");
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
[request]
|
||||
);
|
||||
|
||||
// ─── Provider ─────────────────────────────────────────────────────────────
|
||||
return (
|
||||
<CoursesContext.Provider value={{
|
||||
// state
|
||||
courses, course,
|
||||
units, unit,
|
||||
lessons, lesson,
|
||||
lessonPage,
|
||||
attributes,
|
||||
pagination,
|
||||
loading,
|
||||
|
||||
// setters
|
||||
setPagination,
|
||||
setCourse,
|
||||
setUnit,
|
||||
setLesson,
|
||||
setLessonPage,
|
||||
|
||||
// course actions
|
||||
fetchCourses,
|
||||
fetchCourse,
|
||||
createCourse,
|
||||
updateCourse,
|
||||
deleteCourse,
|
||||
|
||||
// unit actions
|
||||
fetchUnits,
|
||||
fetchUnit,
|
||||
createUnit,
|
||||
updateUnit,
|
||||
deleteUnit,
|
||||
|
||||
// lesson actions
|
||||
fetchLessons,
|
||||
fetchLesson,
|
||||
createLesson,
|
||||
updateLesson,
|
||||
deleteLesson,
|
||||
|
||||
// lesson page actions
|
||||
fetchLessonPage,
|
||||
saveLessonPage,
|
||||
}}>
|
||||
{children}
|
||||
</CoursesContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user