السكرتارية التنفيذية الحديثة وإدارة المكاتب العليا

غير متزامن (مرن) beginner شهادة

Haitham Abdallah
0 طالب
25 ساعة
0
جلسة
0
وحدة
25 ساعة
المدة
مجاني
السعر
السكرتارية التنفيذية الحديثة وإدارة المكاتب العليا

وصف الدورة

السكرتارية التنفيذية الحديثة وإدارة المكاتب العليا
// ===== 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: "اختر تقييم من 1 إلى 5 نجوم", 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 || "تعذر إرسال التقييم", 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: "حسناً" }); } } 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 || "معاينة المحتوى"), 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_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: "المعاينة المجانية", text: "تعذر تحديد الجلسة المطلوبة للمعاينة.", icon: "warning", confirmButtonText: "حسناً" }); return false; } function showPreview() { const lessonId = Number(window.COURSE_PREVIEW_LESSON_ID || 0); if (lessonId > 0) { return goToLearnLesson(lessonId); } Swal.fire({ title: "المعاينة المجانية", text: "اختر درسًا مجانيًا من المنهج بالأسفل للمعاينة.", icon: "info", confirmButtonText: "حسناً" }); } // 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"); } } }