Files
starr-philproperties/src/contexts/ClientCoursesContext.jsx
T
kennethobsequio fbef7cb6e6 ready to test
Testing

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
2026-06-22 10:07:20 +08:00

250 lines
8.7 KiB
React

import { createContext, useCallback, useContext, useState } from "react";
import api from "@/utils/api.util";
import { toast } from "sonner";
// ─── Context ──────────────────────────────────────────────────────────────────
const ClientCoursesContext = createContext(null);
// ─── Provider ─────────────────────────────────────────────────────────────────
export function ClientCoursesProvider({ children }) {
// ── List state
const [courses, setCourses] = useState([]);
const [coursesLoading, setCoursesLoading] = useState(false);
// ── Detail state
const [course, setCourse] = useState(null);
const [courseLoading, setCourseLoading] = useState(false);
const [courseBlocked, setCourseBlocked] = useState(false); // true = 403, prompt upgrade
// ── Unit state
const [unit, setUnit] = useState(null);
const [unitLoading, setUnitLoading] = useState(false);
// ── Lesson state
const [lesson, setLesson] = useState(null);
const [lessonLoading, setLessonLoading] = useState(false);
// ── Quiz state
const [quiz, setQuiz] = useState(null);
const [quizLoading, setQuizLoading] = useState(false);
// ── Assessment state
const [assessment, setAssessment] = useState(null);
const [assessmentLoading, setAssessmentLoading] = useState(false);
// ─── Actions ────────────────────────────────────────────────────────────────
const getCourses = useCallback(async () => {
setCoursesLoading(true);
try {
const { data } = await api.get("/client/courses");
setCourses(data.data ?? []);
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not load courses.");
} finally {
setCoursesLoading(false);
}
}, []);
const getCourse = useCallback(async (courseId) => {
setCourseLoading(true);
setCourseBlocked(false);
try {
const { data } = await api.get(`/client/courses/${courseId}`);
setCourse(data.data ?? null);
} catch (err) {
if (err?.response?.status === 403) {
setCourseBlocked(true); // let the UI show an upgrade prompt
} else {
toast.error(err?.response?.data?.message ?? "Could not load course.");
}
} finally {
setCourseLoading(false);
}
}, []);
const getUnit = useCallback(async (courseId, unitId) => {
setUnitLoading(true);
try {
const { data } = await api.get(`/client/courses/${courseId}/units/${unitId}`);
setUnit(data.data ?? null);
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not load unit.");
} finally {
setUnitLoading(false);
}
}, []);
const getLesson = useCallback(async (courseId, unitId, lessonId) => {
setLessonLoading(true);
try {
const { data } = await api.get(
`/client/courses/${courseId}/units/${unitId}/lessons/${lessonId}`
);
setLesson(data.data ?? null);
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not load lesson.");
} finally {
setLessonLoading(false);
}
}, []);
const getUnitQuiz = useCallback(async (courseId, unitId) => {
setQuizLoading(true);
try {
const { data } = await api.get(
`/client/courses/${courseId}/units/${unitId}/quiz`
);
setQuiz(data.data ?? null);
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not load quiz.");
} finally {
setQuizLoading(false);
}
}, []);
const getCourseAssessment = useCallback(async (courseId) => {
setAssessmentLoading(true);
try {
const { data } = await api.get(`/client/courses/${courseId}/assessment`);
setAssessment(data.data ?? null);
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not load assessment.");
} finally {
setAssessmentLoading(false);
}
}, []);
const submitUnitQuiz = useCallback(async (courseId, unitId, quizId, answers) => {
try {
const { data } = await api.post(
`/client/courses/${courseId}/units/${unitId}/quiz/${quizId}/submit`,
{ answers }
);
return data.data ?? null;
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not submit quiz.");
return null;
}
}, []);
const submitCourseAssessment = useCallback(async (courseId, assessmentId, answers) => {
try {
const { data } = await api.post(
`/client/courses/${courseId}/assessment/${assessmentId}/submit`,
{ answers }
);
return data.data ?? null;
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not submit assessment.");
return null;
}
}, []);
// ─── Course Purchases ───────────────────────────────────────────────────────
const [purchases, setPurchases] = useState([]);
const [purchasesLoading, setPurchasesLoading] = useState(false);
const [purchaseLoading, setPurchaseLoading] = useState(false);
const getMyPurchases = useCallback(async () => {
setPurchasesLoading(true);
try {
const { data } = await api.get("/client/course-purchases");
setPurchases(data.data ?? []);
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not load purchases.");
} finally { setPurchasesLoading(false); }
}, []);
const createCourseOrder = useCallback(async (productId) => {
setPurchaseLoading(true);
try {
const { data } = await api.post("/client/course-purchases/order", { product_id: productId });
return data.data ?? null;
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not create order.");
return null;
} finally { setPurchaseLoading(false); }
}, []);
const captureCourseOrder = useCallback(async (orderId) => {
setPurchaseLoading(true);
try {
const { data } = await api.post("/client/course-purchases/capture", { order_id: orderId });
toast.success("Purchase confirmed! You now have access to this course.");
return data.data ?? null;
} catch (err) {
toast.error(err?.response?.data?.message ?? "Could not capture payment.");
return null;
} finally { setPurchaseLoading(false); }
}, []);
const cancelCourseOrder = useCallback(async (orderId) => {
try {
await api.post("/client/course-purchases/cancel", { order_id: orderId });
} catch { /* silent */ }
}, []);
// ─── Reset helpers ──────────────────────────────────────────────────────────
const resetCourse = useCallback(() => { setCourse(null); setCourseBlocked(false); }, []);
const resetUnit = useCallback(() => setUnit(null), []);
const resetLesson = useCallback(() => setLesson(null), []);
const resetQuiz = useCallback(() => setQuiz(null), []);
const resetAssessment = useCallback(() => setAssessment(null), []);
// ─── Value ──────────────────────────────────────────────────────────────────
const value = {
// state
courses, coursesLoading,
course, courseLoading, courseBlocked,
unit, unitLoading,
lesson, lessonLoading,
quiz, quizLoading,
assessment, assessmentLoading,
// actions
getCourses,
getCourse,
getUnit,
getLesson,
getUnitQuiz,
getCourseAssessment,
submitUnitQuiz,
submitCourseAssessment,
// purchases
purchases, purchasesLoading, purchaseLoading,
getMyPurchases,
createCourseOrder,
captureCourseOrder,
cancelCourseOrder,
// resets
resetCourse,
resetUnit,
resetLesson,
resetQuiz,
resetAssessment,
};
return (
<ClientCoursesContext.Provider value={value}>
{children}
</ClientCoursesContext.Provider>
);
}
// ─── Hook ─────────────────────────────────────────────────────────────────────
export function useClientCourses() {
const ctx = useContext(ClientCoursesContext);
if (!ctx) throw new Error("useClientCourses must be used within ClientCoursesProvider");
return ctx;
}
export default ClientCoursesContext;