Sabron LMS
Education OS
Home
Courses
مميزات المTextة
الباقات
تواصل معنا
دخول
اطلب ديمو
القائمة
Home
Courses
مميزات المTextة
الباقات
دخول
Home
Courses
Beauty, elegance & craftsmanship
معالجات الشعر وطريقة استخدامها
Self-paced
beginner
Certificate
Haitham Abdallah
0 Student
25 hour
0
Session
0
Unit
25 hour
Duration
Free
Price
Sign in to enroll
Course Description
معالجات الشعر وطريقة استخدامها
Curriculum
Enrichment Content
Timeline
Student Reviews (0)
Timeline
Training Days
0 day
Training Hours
25 hour
Total Minutes
1500 دقيقة
Course Summary
Training Type
Self-paced
الCertificate
Available
المستوى
beginner
عدد المراجعات
0
المدرب
Haitham Abdallah
عرض الFile الشخصي
دورات ذات صلة
فن صناعة الشموع
Haitham Abdallah
Free
دورة تجميل الشعر
Haitham Abdallah
Free
دورة تجميل البشرة
Haitham Abdallah
Free
دورة مهارة صناعة البخور وفن تركيب العطور
Haitham Abdallah
Free
// ===== Student Course Tabs ===== (function () { const tabsRoot = document.getElementById("studentCourseTabs"); const panesRoot = document.getElementById("studentCourseTabsContent"); if (!tabsRoot || !panesRoot) return; function activateStudentTab(tabId) { panesRoot.querySelectorAll(".course-tab-pane").forEach((pane) => { pane.classList.add("hidden"); }); tabsRoot.querySelectorAll(".course-tab-btn").forEach((btn) => { btn.classList.remove("active"); btn.setAttribute("aria-selected", "false"); }); const targetPane = document.getElementById(tabId + "-pane"); if (targetPane) targetPane.classList.remove("hidden"); const activeBtn = tabsRoot.querySelector('.course-tab-btn[data-tab="' + tabId + '"]'); if (activeBtn) { activeBtn.classList.add("active"); activeBtn.setAttribute("aria-selected", "true"); } } tabsRoot.addEventListener("click", function (e) { const btn = e.target.closest(".course-tab-btn[data-tab]"); if (!btn) return; e.preventDefault(); activateStudentTab(btn.getAttribute("data-tab")); }); activateStudentTab("curriculum"); })(); // ===== Review Submit (Enrolled Student Only) ===== let myRating = Number(document.getElementById("my_rating")?.value || 0); function setMyRating(v) { myRating = Number(v || 0); const wrap = document.getElementById("myRatingStars"); if (!wrap) return; const btns = wrap.querySelectorAll("button"); btns.forEach((b, idx) => { const on = (idx + 1) <= myRating; b.classList.toggle("text-yellow-400", on); b.classList.toggle("text-gray-300", !on); }); const hidden = document.getElementById("my_rating"); if (hidden) hidden.value = String(myRating); } async function submitMyReview(courseId) { try { // ✅ تعديل: خانة واحدة فقط const review_text = (document.getElementById("review_text")?.value || "").trim(); if (myRating < 1 || myRating > 5) { Toastify({ text: "Choose a rating from 1 to 5 stars", duration: 3000, gravity:"top", position:"right", backgroundColor:"#ef4444" }).showToast(); return; } const fd = new FormData(); fd.append("action", "submit_review"); fd.append("course_id", String(courseId)); fd.append("rating", String(myRating)); fd.append("review_ar", review_text); fd.append("review_en", ""); fd.append("csrf_token", (typeof CSRF_TOKEN !== "undefined" ? CSRF_TOKEN : "")); const res = await fetch(window.location.href, { method: "POST", body: fd, headers: { "X-Requested-With": "XMLHttpRequest", "X-CSRF-Token": (typeof CSRF_TOKEN !== "undefined" ? CSRF_TOKEN : "") } }); const raw = await res.text(); let data; try { data = JSON.parse(raw); } catch (e) { console.error("Non-JSON response:", raw); throw new Error("السيرفر لم يرجع JSON. افتح Console لمعرفة السبب."); } if (!data.success) throw new Error(data.message || "حدث خطأ"); Toastify({ text: data.message || "تم نشر تقييمك", duration: 3000, gravity:"top", position:"right", backgroundColor:"#10b981" }).showToast(); setTimeout(() => location.reload(), 800); } catch (e) { Toastify({ text: e.message || "تعذر Submit Review", duration: 4000, gravity:"top", position:"right", backgroundColor:"#ef4444" }).showToast(); } } // Add to cart async function addToCart(courseId, courseTitle) { try { const response = await fetch(window.SITE_URL + "/api/cart", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-Token": CSRF_TOKEN, "X-Requested-With": "XMLHttpRequest" }, body: JSON.stringify({ action: "add", course_id: courseId }) }); const result = await response.json(); if (!result.success) throw new Error(result.message || "حدث خطأ"); updateCartCounter(result.count); const button = (window.event && window.event.target) ? window.event.target.closest("button") : null; if (button) { button.innerHTML = '
تمت الإضافة للسلة'; button.disabled = true; button.classList.add("opacity-75","cursor-not-allowed"); } Toastify({ text: result.message || "تم إضافة الدورة إلى السلة بنجاح", duration: 3000, gravity: "top", position: "right", backgroundColor: "#10b981", stopOnFocus: true }).showToast(); } catch (error) { Toastify({ text: error.message || "حدث خطأ أثناء إضافة الدورة للسلة", duration: 3000, gravity: "top", position: "right", backgroundColor: "#ef4444", stopOnFocus: true }).showToast(); } } // Remove from cart async function removeFromCart(courseId) { try { const response = await fetch(window.SITE_URL + "/api/cart", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-Token": CSRF_TOKEN, "X-Requested-With": "XMLHttpRequest" }, body: JSON.stringify({ action: "remove", course_id: courseId }) }); const result = await response.json(); if (!result.success) throw new Error(result.message || "حدث خطأ"); updateCartCounter(result.count); location.reload(); } catch (error) { Toastify({ text: error.message || "حدث خطأ أثناء إزالة الدورة من السلة", duration: 3000, gravity: "top", position: "right", backgroundColor: "#ef4444", stopOnFocus: true }).showToast(); } } // Free enroll async function enrollFree(courseId, courseTitle) { try { const response = await fetch(window.SITE_URL + "/api/enroll", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-Token": CSRF_TOKEN, "X-Requested-With": "XMLHttpRequest" }, body: JSON.stringify({ action: "enroll_free", course_id: courseId, csrf_token: CSRF_TOKEN }) }); const result = await response.json(); if (!result.success) throw new Error(result.message || "حدث خطأ أثناء التسجيل"); Swal.fire({ title: "تم التسجيل بنجاح!", text: "تم تسجيلك في الدورة بنجاح. يمكنك الآن البدء في التعلم.", icon: "success", confirmButtonColor: "#10b981", confirmButtonText: "ابدأ التعلم" }).then((r) => { if (r.isConfirmed) { window.location.href = window.SITE_URL + "/learn.php?slug=" + encodeURIComponent(window.COURSE_SLUG); } }); } catch (error) { Swal.fire({ title: "خطأ في التسجيل", text: error.message || "حدث خطأ أثناء التسجيل في الدورة", icon: "error", confirmButtonColor: "#ef4444", confirmButtonText: "OK" }); } } function isAbsoluteUrl(url) { return /^https?:\/\//i.test(String(url || "").trim()); } function buildCourseAssetUrl(path) { let p = String(path || "").trim(); if (!p) return ""; if (isAbsoluteUrl(p)) return p; p = p.replace(/^\/+/, ""); p = p.replace(/^uploads\/courses\/+?/i, ""); return window.UPLOADS_URL.replace(/\/+$/, "") + "/courses/" + p; } function goToLearnLesson(lessonId) { const id = Number(lessonId || 0); const url = new URL(window.SITE_URL.replace(/\/+$/, "") + "/learn.php"); url.searchParams.set("slug", window.COURSE_SLUG); if (id > 0) { url.searchParams.set("lesson", String(id)); } if (!window.COURSE_IS_ENROLLED) { url.searchParams.set("preview", "1"); } window.location.href = url.toString(); return true; } function normalizeCaptionAsset(asset) { if (!asset) return null; const filePath = String(asset.file_path || "").trim(); const fileUrl = String(asset.file_url || "").trim() || buildCourseAssetUrl(filePath); if (!fileUrl) return null; return { label: String(asset.title_ar || asset.asset_title || asset.title || "CC"), srclang: String(asset.language_code || "ar").toLowerCase(), src: fileUrl, default: Number(asset.is_primary || 0) === 1 }; } function normalizePreviewPayload(rawPayload) { const p = Object.assign({}, rawPayload || {}); const assets = Array.isArray(p.assets) ? p.assets : []; const type = String(p.type || p.content_type || "").toLowerCase(); const filePath = String(p.file_path || "").trim(); const fileUrl = String(p.file_url || "").trim() || buildCourseAssetUrl(filePath); const videoUrl = String(p.video_url || "").trim(); const externalUrl = String(p.external_url || p.meeting_link || "").trim(); const normalized = { id: Number(p.id || 0), title: String(p.title || p.asset_title || "معاينة الContent"), type: type, asset_type: type, video_type: String(p.video_type || ""), video_url: videoUrl, file_path: filePath, file_url: fileUrl, external_url: externalUrl, meeting_link: String(p.meeting_link || ""), content_ar: String(p.content_ar || ""), content_text: String(p.content_text || p.content_ar || ""), transcript: "", transcript_text: "", audio_description: "", audio_description_text: "", captions: [], captions_assets: [] }; // لو النوع غير موجود لكن يوجد file/video/url if (!normalized.type) { if (videoUrl || filePath) normalized.type = "video"; else if (externalUrl) normalized.type = "url"; else if (normalized.content_text) normalized.type = "text"; } // لو Video مرفوع وvideo_type فاضي if (normalized.type === "video" && filePath && !normalized.video_type) { normalized.video_type = "upload"; } // قراءة أصول الوصول من assets if (assets.length) { const primary = assets.find(a => Number(a && a.is_primary || 0) === 1) || assets[0] || null; if (primary) { const primaryType = String(primary.asset_type || "").toLowerCase(); if (primaryType) { normalized.type = primaryType === "attachment" ? "file" : primaryType; normalized.asset_type = normalized.type; } if (!normalized.video_type && primary.video_type) normalized.video_type = String(primary.video_type); if (!normalized.video_url && primary.video_url) normalized.video_url = String(primary.video_url); if (!normalized.file_path && primary.file_path) normalized.file_path = String(primary.file_path); if (!normalized.file_url && primary.file_path) normalized.file_url = buildCourseAssetUrl(primary.file_path); if (!normalized.external_url && primary.external_url) normalized.external_url = String(primary.external_url); if (!normalized.content_text && primary.content_text) normalized.content_text = String(primary.content_text); } const transcriptAsset = assets.find(a => String(a && a.asset_type || "").toLowerCase() === "transcript"); if (transcriptAsset) { const tx = String(transcriptAsset.content_text || transcriptAsset.content_ar || "").trim(); if (tx) { normalized.transcript = tx; normalized.transcript_text = tx; } } const audioDescAsset = assets.find(a => String(a && a.asset_type || "").toLowerCase() === "audio_description"); if (audioDescAsset) { const ad = String(audioDescAsset.content_text || audioDescAsset.content_ar || "").trim(); if (ad) { normalized.audio_description = ad; normalized.audio_description_text = ad; } } const captionAssets = assets .filter(a => { const t = String(a && a.asset_type || "").toLowerCase(); return t === "captions" || t === "cc"; }) .map(normalizeCaptionAsset) .filter(Boolean); if (captionAssets.length) { normalized.captions = captionAssets; normalized.captions_assets = captionAssets.map(item => ({ label: item.label, language_code: item.srclang, file_url: item.src, default: item.default ? 1 : 0 })); } } // fallback transcript من content_ar لو لا يوجد transcript خاص if (!normalized.transcript && normalized.content_text) { normalized.transcript = normalized.content_text; normalized.transcript_text = normalized.content_text; } // تحويل بعض الأنواع if (normalized.type === "attachment") normalized.type = "file"; if (normalized.type === "live_recording") normalized.type = "file"; return normalized; } function openLessonPreview(p) { const lessonId = Number((p && (p.lesson_id || p.id)) || 0); if (lessonId > 0) { return goToLearnLesson(lessonId); } Swal.fire({ title: "المعاينة الFreeة", text: "Could not identify the requested preview session.", icon: "warning", confirmButtonText: "OK" }); return false; } function showPreview() { const lessonId = Number(window.COURSE_PREVIEW_LESSON_ID || 0); if (lessonId > 0) { return goToLearnLesson(lessonId); } Swal.fire({ title: "المعاينة الFreeة", text: "Choose a free lesson from the curriculum below for preview.", icon: "info", confirmButtonText: "OK" }); } // Update cart counter function updateCartCounter(count) { const cartCount = document.querySelector(".cart-count"); if (cartCount) cartCount.textContent = count; const cartBadge = document.querySelector(".cart-badge"); if (cartBadge) { if (count > 0) { cartBadge.style.display = "inline-flex"; cartBadge.classList.remove("hidden"); } else { cartBadge.style.display = "none"; cartBadge.classList.add("hidden"); } } }