Octopus-React-Wp/frontend/src/components/Admin/CoursLecture.jsx

288 lines
8.0 KiB
JavaScript

import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { Box, Typography, CircularProgress } from "@mui/material";
import { MoodleApi } from "../../services/moodleApi";
const PUBLIC_LINKS_STORAGE_KEY = "listeCoursPublicLinks";
const CoursLecture = () => {
const { token } = useParams();
const [content, setContent] = useState(null);
const [title, setTitle] = useState("");
const [loading, setLoading] = useState(true);
const [tokenValid, setTokenValid] = useState(true);
// ✅ Masquer Header/Footer pendant l'affichage
useEffect(() => {
if (typeof document === "undefined") {
return () => {};
}
const header = document.querySelector("header");
const footer = document.querySelector("footer");
const previousHeaderDisplay = header?.style.display;
const previousFooterDisplay = footer?.style.display;
if (header) header.style.display = "none";
if (footer) footer.style.display = "none";
const handleContextMenu = (event) => event.preventDefault();
const handleCopyPaste = (event) => event.preventDefault();
document.addEventListener("contextmenu", handleContextMenu);
document.addEventListener("copy", handleCopyPaste);
document.addEventListener("cut", handleCopyPaste);
document.addEventListener("paste", handleCopyPaste);
const previousUserSelect = document.body.style.userSelect;
document.body.style.userSelect = "none";
return () => {
if (header) header.style.display = previousHeaderDisplay || "";
if (footer) footer.style.display = previousFooterDisplay || "";
document.removeEventListener("contextmenu", handleContextMenu);
document.removeEventListener("copy", handleCopyPaste);
document.removeEventListener("cut", handleCopyPaste);
document.removeEventListener("paste", handleCopyPaste);
document.body.style.userSelect = previousUserSelect || "auto";
};
}, []);
// ✅ Récupération contenu
useEffect(() => {
const decodeToken = () => {
if (!token) {
return null;
}
try {
const raw = atob(token);
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object") {
throw new Error("Invalid JSON payload");
}
const {
courseId,
itemType = "page",
itemId,
expiry,
linkId = null,
} = parsed;
if (!courseId || !itemId || !expiry) {
return null;
}
return {
courseId: String(courseId),
itemType: itemType || "page",
itemId: String(itemId),
expiry,
linkId,
format: "json",
};
} catch (parseError) {
console.warn("Lien public JSON invalide :", parseError);
const parts = raw.split("_");
if (parts.length === 3) {
const [courseId, itemId, expiry] = parts;
return {
courseId,
itemType: "page",
itemId,
expiry,
linkId: null,
format: "legacy",
};
}
if (parts.length === 4) {
const [courseId, itemType, itemId, expiry] = parts;
return {
courseId,
itemType,
itemId,
expiry,
linkId: null,
format: "legacy",
};
}
return null;
}
} catch (decodeError) {
console.warn("Impossible de décoder le lien sécurisé :", decodeError);
return null;
}
};
const decoded = decodeToken();
if (!decoded) {
setTokenValid(false);
setContent("⛔ Lien invalide.");
setLoading(false);
return;
}
const { courseId, itemType, itemId, expiry, format } = decoded;
const expiryValue = Number(expiry);
if (
!courseId ||
!itemId ||
!Number.isFinite(expiryValue) ||
Date.now() > expiryValue
) {
setTokenValid(false);
setContent("⛔ Le cours est terminé !");
setLoading(false);
return;
}
const normalizedItemType = itemType || "page";
const linkStillActive = () => {
if (format === "legacy") {
return true;
}
if (typeof window === "undefined") {
return true;
}
try {
const stored = localStorage.getItem(PUBLIC_LINKS_STORAGE_KEY);
if (!stored) {
return false;
}
const registry = JSON.parse(stored);
const key = `${courseId}-${normalizedItemType}-${itemId}`;
const entry = registry?.[key];
if (!entry || !entry.active) {
return false;
}
if (entry.token && entry.token !== token) {
return false;
}
if (
entry.expiry !== undefined &&
Number.isFinite(Number(entry.expiry)) &&
Date.now() > Number(entry.expiry)
) {
return false;
}
return true;
} catch (error) {
console.error("Impossible de vérifier l'état du lien public :", error);
return false;
}
};
if (!linkStillActive()) {
setTokenValid(false);
setContent("⛔ Ce lien n'est plus actif.");
setLoading(false);
return;
}
const fetchPage = async () => {
try {
const pages = await MoodleApi.getPagesByCourse(Number(courseId));
const parsedItemId = parseInt(itemId, 10);
const found = pages.find((p) => p.id === parsedItemId);
if (found) {
setTitle(found.name || "");
setContent(found.content || "");
} else {
setTokenValid(false);
setContent("⛔ Page non trouvée.");
}
} catch (error) {
console.error("Erreur lors du chargement de la page Moodle :", error);
setTokenValid(false);
setContent("❌ Erreur lors du chargement.");
} finally {
setLoading(false);
}
};
const fetchAssignment = async () => {
try {
const assignments = await MoodleApi.getAssignmentsByCourse(
Number(courseId)
);
const parsedAssignmentId = parseInt(itemId, 10);
const assignment = assignments.find(
(a) => a.id === parsedAssignmentId
);
if (assignment) {
setTitle(assignment.name || "");
setContent(assignment.intro || "<p>Aucun contenu disponible.</p>");
} else {
setTokenValid(false);
setContent("⛔ Devoir introuvable.");
}
} catch (error) {
console.error(
"Erreur lors du chargement du devoir Moodle :",
error
);
setTokenValid(false);
setContent("❌ Erreur lors du chargement du devoir.");
} finally {
setLoading(false);
}
};
if (normalizedItemType === "assignment") {
fetchAssignment();
} else {
fetchPage();
}
}, [token]);
if (loading) {
return (
<Box
sx={{
padding: 4,
textAlign: "center",
userSelect: "none",
WebkitUserSelect: "none",
MozUserSelect: "none",
msUserSelect: "none",
}}
>
<CircularProgress />
</Box>
);
}
if (!tokenValid) {
return (
<Box
sx={{
height: "100vh",
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f8d7da",
}}
>
<Typography
variant="h3"
sx={{
color: "#721c24",
fontWeight: "bold",
textAlign: "center",
padding: 4,
}}
>
{content}
</Typography>
</Box>
);
}
return (
<Box sx={{ padding: 4, mt: 5 }}>
{title ? (
<Typography variant="h4" sx={{ mb: 3, color: "#315397" }}>
{title}
</Typography>
) : null}
<div dangerouslySetInnerHTML={{ __html: content }} />
</Box>
);
};
export default CoursLecture;