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 ( {children} ); } // ─── Hook ───────────────────────────────────────────────────────────────────── export function useClientCourses() { const ctx = useContext(ClientCoursesContext); if (!ctx) throw new Error("useClientCourses must be used within ClientCoursesProvider"); return ctx; } export default ClientCoursesContext;