mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
@@ -23,6 +23,22 @@ const PAGINATION_INIT = {
|
||||
|
||||
const BASE = "/admin/courses";
|
||||
|
||||
// Junction revamp — unit-scoped endpoints resolve either through a course
|
||||
// (/admin/courses/:courseId/units/:unitId) or straight against the standalone
|
||||
// unit library (/admin/units/:unitId) when no courseId is in scope. This lets
|
||||
// the quiz builder and unit pages run identically in both contexts.
|
||||
const unitBase = (courseId, unitId) =>
|
||||
courseId != null && courseId !== ""
|
||||
? `${BASE}/${courseId}/units/${unitId}`
|
||||
: `/admin/units/${unitId}`;
|
||||
|
||||
// Lesson-scoped endpoints resolve through a unit when one is in scope,
|
||||
// otherwise straight against the standalone lesson library (/admin/lessons/:id).
|
||||
const lessonBase = (courseId, unitId, lessonId) =>
|
||||
unitId != null && unitId !== ""
|
||||
? `${unitBase(courseId, unitId)}/lessons/${lessonId}`
|
||||
: `/admin/lessons/${lessonId}`;
|
||||
|
||||
// ─── Provider ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export function CoursesProvider({ children }) {
|
||||
@@ -288,7 +304,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchUnit = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}`);
|
||||
const result = data?.data?.data ?? null;
|
||||
setUnit(result);
|
||||
setLessons(result?.lessons ?? []);
|
||||
@@ -315,7 +331,7 @@ export function CoursesProvider({ children }) {
|
||||
const updateUnit = useCallback(
|
||||
(courseId, unitId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.put(`${BASE}/${courseId}/units/${unitId}`, payload);
|
||||
const { data } = await api.put(`${unitBase(courseId, unitId)}`, payload);
|
||||
const unit = data?.data?.data ?? null;
|
||||
if (unit) {
|
||||
setUnits((prev) => prev.map((u) => (u.unit_id === unitId ? unit : u)));
|
||||
@@ -330,7 +346,7 @@ export function CoursesProvider({ children }) {
|
||||
const archiveUnit = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}`);
|
||||
const { data } = await api.delete(`${unitBase(courseId, unitId)}`);
|
||||
setUnits((prev) => prev.filter((u) => u.unit_id !== unitId));
|
||||
setUnit((prev) => (prev?.unit_id === unitId ? null : prev));
|
||||
toast("Unit archived.");
|
||||
@@ -372,7 +388,7 @@ export function CoursesProvider({ children }) {
|
||||
const restoreUnit = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/restore`);
|
||||
const { data } = await api.patch(`${unitBase(courseId, unitId)}/restore`);
|
||||
const result = data?.data?.data ?? null;
|
||||
if (result) {
|
||||
setUnits((prev) => prev.filter((u) => u.unit_id !== unitId));
|
||||
@@ -397,7 +413,7 @@ export function CoursesProvider({ children }) {
|
||||
const permanentlyDeleteUnit = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/permanent`);
|
||||
const { data } = await api.delete(`${unitBase(courseId, unitId)}/permanent`);
|
||||
setUnits((prev) => prev.filter((u) => u.unit_id !== unitId));
|
||||
setUnit((prev) => (prev?.unit_id === unitId ? null : prev));
|
||||
toast("Unit permanently deleted.");
|
||||
@@ -420,7 +436,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchUnitPermanentDeleteImpact = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/permanent-delete-impact`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/permanent-delete-impact`);
|
||||
const { lessonCount } = data?.data ?? {};
|
||||
return [{ label: "lesson(s)", count: lessonCount ?? 0 }];
|
||||
}),
|
||||
@@ -434,7 +450,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchQuiz = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/quiz`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/quiz`);
|
||||
const result = data?.data?.data ?? null;
|
||||
setQuiz(result);
|
||||
setQuestions(result?.questions ?? []);
|
||||
@@ -446,7 +462,7 @@ export function CoursesProvider({ children }) {
|
||||
const createQuiz = useCallback(
|
||||
(courseId, unitId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.post(`${BASE}/${courseId}/units/${unitId}/quiz`, payload);
|
||||
const { data } = await api.post(`${unitBase(courseId, unitId)}/quiz`, payload);
|
||||
const result = data?.data?.data ?? null;
|
||||
if (result) {
|
||||
setQuiz(result);
|
||||
@@ -460,7 +476,7 @@ export function CoursesProvider({ children }) {
|
||||
const updateQuiz = useCallback(
|
||||
(courseId, unitId, quizId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}`, payload);
|
||||
const { data } = await api.patch(`${unitBase(courseId, unitId)}/quiz/${quizId}`, payload);
|
||||
const result = data?.data?.data ?? null;
|
||||
if (result) {
|
||||
setQuiz(result);
|
||||
@@ -474,7 +490,7 @@ export function CoursesProvider({ children }) {
|
||||
const deleteQuiz = useCallback(
|
||||
(courseId, unitId, quizId, deletedBy) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}`, { data: { deletedBy } });
|
||||
const { data } = await api.delete(`${unitBase(courseId, unitId)}/quiz/${quizId}`, { data: { deletedBy } });
|
||||
setQuiz(null);
|
||||
setQuestions([]);
|
||||
toast("Quiz archived.");
|
||||
@@ -488,7 +504,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchArchivedQuiz = useCallback(
|
||||
(courseId, unitId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/quiz/archives`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/quiz/archives`);
|
||||
const result = data?.data?.data ?? null;
|
||||
setQuiz(result);
|
||||
return data;
|
||||
@@ -499,7 +515,7 @@ export function CoursesProvider({ children }) {
|
||||
const restoreQuiz = useCallback(
|
||||
(courseId, unitId, quizId, restoredBy) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/restore`, { restoredBy });
|
||||
const { data } = await api.patch(`${unitBase(courseId, unitId)}/quiz/${quizId}/restore`, { restoredBy });
|
||||
const result = data?.data?.data ?? null;
|
||||
if (result) {
|
||||
setQuiz(result);
|
||||
@@ -517,7 +533,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchQuizQuestions = useCallback(
|
||||
(courseId, unitId, quizId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions`);
|
||||
const result = data?.data?.data ?? [];
|
||||
setQuestions(result);
|
||||
return data;
|
||||
@@ -528,7 +544,7 @@ export function CoursesProvider({ children }) {
|
||||
const createQuizQuestion = useCallback(
|
||||
(courseId, unitId, quizId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.post(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions`, payload);
|
||||
const { data } = await api.post(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions`, payload);
|
||||
const result = data?.data?.data ?? null;
|
||||
if (result) {
|
||||
setQuestions((prev) => [...prev, result]);
|
||||
@@ -542,7 +558,7 @@ export function CoursesProvider({ children }) {
|
||||
const updateQuizQuestion = useCallback(
|
||||
(courseId, unitId, quizId, questionId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions/${questionId}`, payload);
|
||||
const { data } = await api.patch(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/${questionId}`, payload);
|
||||
const result = data?.data?.data ?? null;
|
||||
if (result) {
|
||||
setQuestions((prev) => prev.map((q) => (q.question_id === questionId ? result : q)));
|
||||
@@ -556,7 +572,7 @@ export function CoursesProvider({ children }) {
|
||||
const bulkSyncQuizQuestions = useCallback(
|
||||
(courseId, unitId, quizId, questions, updatedBy) =>
|
||||
request(async () => {
|
||||
const { data } = await api.put(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions/bulk-sync`, { questions, updatedBy });
|
||||
const { data } = await api.put(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/bulk-sync`, { questions, updatedBy });
|
||||
const result = data?.data?.data ?? [];
|
||||
setQuestions(result);
|
||||
toast("Quiz saved.");
|
||||
@@ -568,7 +584,7 @@ export function CoursesProvider({ children }) {
|
||||
const deleteQuizQuestion = useCallback(
|
||||
(courseId, unitId, quizId, questionId, deletedBy) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions/${questionId}`, { data: { deletedBy } });
|
||||
const { data } = await api.delete(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/${questionId}`, { data: { deletedBy } });
|
||||
setQuestions((prev) => prev.filter((q) => q.question_id !== questionId));
|
||||
toast("Question archived.");
|
||||
return data;
|
||||
@@ -579,7 +595,7 @@ export function CoursesProvider({ children }) {
|
||||
const bulkArchiveQuizQuestions = useCallback(
|
||||
(courseId, unitId, quizId, ids, deletedBy) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions/bulk`, { data: { ids, deletedBy } });
|
||||
const { data } = await api.delete(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/bulk`, { data: { ids, deletedBy } });
|
||||
setQuestions((prev) => prev.filter((q) => !ids.includes(q.question_id)));
|
||||
toast("Questions archived.");
|
||||
return data;
|
||||
@@ -592,7 +608,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchArchivedQuizQuestion = useCallback(
|
||||
(courseId, unitId, quizId, questionId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions/archives/${questionId}`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/archives/${questionId}`);
|
||||
return data;
|
||||
}),
|
||||
[request],
|
||||
@@ -601,7 +617,7 @@ export function CoursesProvider({ children }) {
|
||||
const restoreQuizQuestion = useCallback(
|
||||
(courseId, unitId, quizId, questionId, restoredBy) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions/${questionId}/restore`, { restoredBy });
|
||||
const { data } = await api.patch(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/${questionId}/restore`, { restoredBy });
|
||||
toast("Question restored.");
|
||||
return data;
|
||||
}),
|
||||
@@ -611,7 +627,7 @@ export function CoursesProvider({ children }) {
|
||||
const bulkRestoreQuizQuestions = useCallback(
|
||||
(courseId, unitId, quizId, ids, restoredBy) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/questions/restore/bulk`, { ids, restoredBy });
|
||||
const { data } = await api.patch(`${unitBase(courseId, unitId)}/quiz/${quizId}/questions/restore/bulk`, { ids, restoredBy });
|
||||
toast("Questions restored.");
|
||||
return data;
|
||||
}),
|
||||
@@ -624,14 +640,14 @@ export function CoursesProvider({ children }) {
|
||||
|
||||
const fetchLessons = useCallback(
|
||||
(courseId, unitId, params = {}) =>
|
||||
paginatedGet(`${BASE}/${courseId}/units/${unitId}/lessons`, setLessons, params),
|
||||
paginatedGet(`${unitBase(courseId, unitId)}/lessons`, setLessons, params),
|
||||
[paginatedGet],
|
||||
);
|
||||
|
||||
const fetchLesson = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}`);
|
||||
const { data } = await api.get(`${lessonBase(courseId, unitId, lessonId)}`);
|
||||
const result = data?.data?.data ?? null;
|
||||
setLesson(result);
|
||||
setLessonPage(result?.page ?? null);
|
||||
@@ -643,7 +659,7 @@ export function CoursesProvider({ children }) {
|
||||
const createLesson = useCallback(
|
||||
(courseId, unitId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.post(`${BASE}/${courseId}/units/${unitId}/lessons`, payload);
|
||||
const { data } = await api.post(`${unitBase(courseId, unitId)}/lessons`, payload);
|
||||
const lesson = data?.data?.data ?? null;
|
||||
if (lesson) {
|
||||
setLessons((prev) => [...prev, lesson]);
|
||||
@@ -657,7 +673,7 @@ export function CoursesProvider({ children }) {
|
||||
const updateLesson = useCallback(
|
||||
(courseId, unitId, lessonId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.put(`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}`, payload);
|
||||
const { data } = await api.put(`${lessonBase(courseId, unitId, lessonId)}`, payload);
|
||||
const lesson = data?.data?.data ?? null;
|
||||
if (lesson) {
|
||||
setLessons((prev) => prev.map((l) => (l.lesson_id === lessonId ? lesson : l)));
|
||||
@@ -672,7 +688,7 @@ export function CoursesProvider({ children }) {
|
||||
const archiveLesson = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}`);
|
||||
const { data } = await api.delete(`${lessonBase(courseId, unitId, lessonId)}`);
|
||||
setLessons((prev) => prev.filter((l) => l.lesson_id !== lessonId));
|
||||
setLesson((prev) => (prev?.lesson_id === lessonId ? null : prev));
|
||||
toast("Lesson archived.");
|
||||
@@ -684,7 +700,7 @@ export function CoursesProvider({ children }) {
|
||||
const archiveLessons = useCallback(
|
||||
(courseId, unitId, { ids }) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/lessons/bulk`, { data: { ids } });
|
||||
const { data } = await api.delete(`${unitBase(courseId, unitId)}/lessons/bulk`, { data: { ids } });
|
||||
setLessons((prev) => prev.filter((l) => !ids.includes(l.lesson_id)));
|
||||
toast("Lessons archived.");
|
||||
return data;
|
||||
@@ -696,14 +712,14 @@ export function CoursesProvider({ children }) {
|
||||
|
||||
const fetchArchivedLessons = useCallback(
|
||||
(courseId, unitId, params = {}) =>
|
||||
paginatedGet(`${BASE}/${courseId}/units/${unitId}/lessons/archives`, setLessons, params),
|
||||
paginatedGet(`${unitBase(courseId, unitId)}/lessons/archives`, setLessons, params),
|
||||
[paginatedGet],
|
||||
);
|
||||
|
||||
const fetchArchivedLesson = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/lessons/archives/${lessonId}`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/lessons/archives/${lessonId}`);
|
||||
const result = data?.data?.data ?? null;
|
||||
setLesson(result);
|
||||
return data;
|
||||
@@ -714,7 +730,7 @@ export function CoursesProvider({ children }) {
|
||||
const restoreLesson = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}/restore`);
|
||||
const { data } = await api.patch(`${lessonBase(courseId, unitId, lessonId)}/restore`);
|
||||
const result = data?.data?.data ?? null;
|
||||
if (result) {
|
||||
setLessons((prev) => prev.filter((l) => l.lesson_id !== lessonId));
|
||||
@@ -728,7 +744,7 @@ export function CoursesProvider({ children }) {
|
||||
const restoreLessons = useCallback(
|
||||
(courseId, unitId, { ids }) =>
|
||||
request(async () => {
|
||||
const { data } = await api.patch(`${BASE}/${courseId}/units/${unitId}/lessons/restore/bulk`, { ids });
|
||||
const { data } = await api.patch(`${unitBase(courseId, unitId)}/lessons/restore/bulk`, { ids });
|
||||
setLessons((prev) => prev.filter((l) => !ids.includes(l.lesson_id)));
|
||||
toast("Lessons restored.");
|
||||
return data;
|
||||
@@ -739,7 +755,7 @@ export function CoursesProvider({ children }) {
|
||||
const permanentlyDeleteLesson = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}/permanent`);
|
||||
const { data } = await api.delete(`${lessonBase(courseId, unitId, lessonId)}/permanent`);
|
||||
setLessons((prev) => prev.filter((l) => l.lesson_id !== lessonId));
|
||||
setLesson((prev) => (prev?.lesson_id === lessonId ? null : prev));
|
||||
toast("Lesson permanently deleted.");
|
||||
@@ -751,7 +767,7 @@ export function CoursesProvider({ children }) {
|
||||
const permanentlyDeleteLessons = useCallback(
|
||||
(courseId, unitId, { ids }) =>
|
||||
request(async () => {
|
||||
const { data } = await api.delete(`${BASE}/${courseId}/units/${unitId}/lessons/bulk/permanent`, { data: { ids } });
|
||||
const { data } = await api.delete(`${unitBase(courseId, unitId)}/lessons/bulk/permanent`, { data: { ids } });
|
||||
setLessons((prev) => prev.filter((l) => !ids.includes(l.lesson_id)));
|
||||
toast("Lessons permanently deleted.");
|
||||
return data;
|
||||
@@ -766,7 +782,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchLessonPage = useCallback(
|
||||
(courseId, unitId, lessonId) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}/page`);
|
||||
const { data } = await api.get(`${lessonBase(courseId, unitId, lessonId)}/page`);
|
||||
const page = data?.data?.data ?? null;
|
||||
setLessonPage(page);
|
||||
return data;
|
||||
@@ -777,7 +793,7 @@ export function CoursesProvider({ children }) {
|
||||
const saveLessonPage = useCallback(
|
||||
(courseId, unitId, lessonId, payload) =>
|
||||
request(async () => {
|
||||
const { data } = await api.put(`${BASE}/${courseId}/units/${unitId}/lessons/${lessonId}/page`, payload);
|
||||
const { data } = await api.put(`${lessonBase(courseId, unitId, lessonId)}/page`, payload);
|
||||
const page = data?.data?.data ?? null;
|
||||
if (page) {
|
||||
setLessonPage(page);
|
||||
@@ -987,7 +1003,7 @@ export function CoursesProvider({ children }) {
|
||||
(courseId, unitId, quizId) =>
|
||||
request(async () => {
|
||||
setCompletions(null);
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/quiz/${quizId}/completions`);
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/quiz/${quizId}/completions`);
|
||||
setCompletions(data?.data ?? null);
|
||||
return data;
|
||||
}),
|
||||
@@ -1109,7 +1125,7 @@ export function CoursesProvider({ children }) {
|
||||
const fetchLessonFieldValues = useCallback(
|
||||
(courseId, unitId, field) =>
|
||||
request(async () => {
|
||||
const { data } = await api.get(`${BASE}/${courseId}/units/${unitId}/field-values`, { params: { field } });
|
||||
const { data } = await api.get(`${unitBase(courseId, unitId)}/field-values`, { params: { field } });
|
||||
const result = data?.data ?? [];
|
||||
return result;
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user