creation coursLecture
This commit is contained in:
+30
-12
@@ -1,4 +1,4 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
@@ -6,20 +6,38 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<!-- ✅ Ajout Open Graph statique -->
|
<!-- ✅ Ajout Open Graph statique -->
|
||||||
<meta property="og:title" content="Titre par défaut - Centre de formations">
|
<meta
|
||||||
<meta property="og:description" content="Bienvenue sur notre Centre de formations aux métiers du numérique.">
|
property="og:title"
|
||||||
<meta property="og:image" content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif">
|
content="Titre par défaut - Centre de formations"
|
||||||
<meta property="og:type" content="website">
|
/>
|
||||||
<meta property="og:url" content="https://ton-site.com">
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Bienvenue sur notre Centre de formations aux métiers du numérique."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"
|
||||||
|
/>
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://ton-site.com" />
|
||||||
|
|
||||||
<!-- ✅ Twitter Cards -->
|
<!-- ✅ Twitter Cards -->
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="twitter:title" content="Titre par défaut - Centre de formations">
|
<meta
|
||||||
<meta name="twitter:description" content="Bienvenue sur notre Centre de formations aux métiers du numérique.">
|
name="twitter:title"
|
||||||
<meta name="twitter:image" content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif">
|
content="Titre par défaut - Centre de formations"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="Bienvenue sur notre Centre de formations aux métiers du numérique."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"
|
||||||
|
/>
|
||||||
<!-- <link rel="preload" href="/src/assets/vendor-BFTbvl5C.js" as="script"> -->
|
<!-- <link rel="preload" href="/src/assets/vendor-BFTbvl5C.js" as="script"> -->
|
||||||
<link rel="preload" href="/assets/index-CfManBR6.css" as="style">
|
<link rel="preload" href="/assets/index-CfManBR6.css" as="style" />
|
||||||
<link rel="stylesheet" href="/assets/index-CfManBR6.css">
|
<link rel="stylesheet" href="/assets/index-CfManBR6.css" />
|
||||||
<script src="https://unpkg.com/html2pdf.js@0.10.1/dist/html2pdf.bundle.min.js"></script>
|
<script src="https://unpkg.com/html2pdf.js@0.10.1/dist/html2pdf.bundle.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -21,6 +21,42 @@ h2,h3,h4,h5,h6,span{
|
|||||||
color: #315397 !important;
|
color: #315397 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c4lv-readingcontext p, .c4lv-readingcontext {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c4lv-dodontcards .c4l-dodontcards-do {
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 100%;
|
||||||
|
background: #f1fbf5;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 24px 48px 30px 36px;
|
||||||
|
margin: 12px auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.c4lv-dodontcards .c4l-dodontcards-do::before {
|
||||||
|
content: url(https://www.formations.octopusdesign.fr/theme/image.php/boost_magnific/tiny_c4l/1752797278/c4l_docard);
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
.c4lv-dodontcards .c4l-dodontcards-dont {
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 100%;
|
||||||
|
background: #ffefef;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 24px 48px 30px 36px;
|
||||||
|
margin: 12px auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.c4lv-dodontcards .c4l-dodontcards-dont::before {
|
||||||
|
content: url(https://www.formations.octopusdesign.fr/theme/image.php/boost_magnific/tiny_c4l/1752797278/c4l_dontcard);
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
h3{
|
h3{
|
||||||
font-size: 1.2em !important;
|
font-size: 1.2em !important;
|
||||||
}
|
}
|
||||||
@@ -57,4 +93,18 @@ ul li{
|
|||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #315397;
|
color: #315397;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c4lv-readingcontext {
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 75%;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 4px 24px rgb(0 0 0 / .08);
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 36px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c4lv-readingcontext {
|
||||||
|
padding: 30px 40px 32px 40px;
|
||||||
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import axios from "axios";
|
||||||
|
import { Box, Typography, CircularProgress } from "@mui/material";
|
||||||
|
|
||||||
|
const API_URL =
|
||||||
|
"https://www.formations.octopusdesign.fr/webservice/rest/server.php";
|
||||||
|
const TOKEN = "685e1b5d794b558b60e971581154c3b2";
|
||||||
|
|
||||||
|
const CoursLecture = () => {
|
||||||
|
// Empêche le clic droit
|
||||||
|
const handleContextMenu = (e) => e.preventDefault();
|
||||||
|
document.addEventListener("contextmenu", handleContextMenu);
|
||||||
|
|
||||||
|
// Empêche le copier/coller
|
||||||
|
const handleCopyPaste = (e) => e.preventDefault();
|
||||||
|
document.addEventListener("copy", handleCopyPaste);
|
||||||
|
document.addEventListener("cut", handleCopyPaste);
|
||||||
|
document.addEventListener("paste", handleCopyPaste);
|
||||||
|
|
||||||
|
// Empêche la sélection de texte
|
||||||
|
document.body.style.userSelect = "none";
|
||||||
|
|
||||||
|
const { token } = useParams();
|
||||||
|
const [content, setContent] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [tokenValid, setTokenValid] = useState(true);
|
||||||
|
|
||||||
|
// ✅ Masquer Header/Footer pendant l'affichage
|
||||||
|
useEffect(() => {
|
||||||
|
const header = document.querySelector("header");
|
||||||
|
const footer = document.querySelector("footer");
|
||||||
|
if (header) header.style.display = "none";
|
||||||
|
if (footer) footer.style.display = "none";
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (header) header.style.display = "";
|
||||||
|
if (footer) footer.style.display = "";
|
||||||
|
document.removeEventListener("contextmenu", handleContextMenu);
|
||||||
|
document.removeEventListener("copy", handleCopyPaste);
|
||||||
|
document.removeEventListener("cut", handleCopyPaste);
|
||||||
|
document.removeEventListener("paste", handleCopyPaste);
|
||||||
|
document.body.style.userSelect = "auto";
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// ✅ Récupération contenu
|
||||||
|
useEffect(() => {
|
||||||
|
const [courseId, pageId, expiry] = atob(token).split("_");
|
||||||
|
|
||||||
|
if (!courseId || !pageId || !expiry || Date.now() > parseInt(expiry)) {
|
||||||
|
setTokenValid(false);
|
||||||
|
setContent("⛔ Le cours est terminé !");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchPage = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get(API_URL, {
|
||||||
|
params: {
|
||||||
|
wstoken: TOKEN,
|
||||||
|
wsfunction: "mod_page_get_pages_by_courses",
|
||||||
|
moodlewsrestformat: "json",
|
||||||
|
courseids: [parseInt(courseId)],
|
||||||
|
},
|
||||||
|
paramsSerializer: (params) => {
|
||||||
|
const sp = new URLSearchParams();
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((v, i) => sp.append(`${key}[${i}]`, v));
|
||||||
|
} else {
|
||||||
|
sp.append(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return sp.toString();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const found = res.data.pages.find((p) => p.id === parseInt(pageId));
|
||||||
|
if (found) {
|
||||||
|
setContent(found.content);
|
||||||
|
} else {
|
||||||
|
setTokenValid(false);
|
||||||
|
setContent("⛔ Page non trouvée.");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setTokenValid(false);
|
||||||
|
setContent("❌ Erreur lors du chargement.");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 }}>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: content }} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CoursLecture;
|
||||||
@@ -20,9 +20,10 @@ import {
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { getToken } from "../../auth";
|
import { getToken } from "../../auth";
|
||||||
import '../../assets/styleCours.css';
|
import "../../assets/styleCours.css";
|
||||||
|
|
||||||
const API_URL = "https://www.formations.octopusdesign.fr/webservice/rest/server.php";
|
const API_URL =
|
||||||
|
"https://www.formations.octopusdesign.fr/webservice/rest/server.php";
|
||||||
const TOKEN = "685e1b5d794b558b60e971581154c3b2";
|
const TOKEN = "685e1b5d794b558b60e971581154c3b2";
|
||||||
|
|
||||||
const ListeCours = () => {
|
const ListeCours = () => {
|
||||||
@@ -40,17 +41,26 @@ const ListeCours = () => {
|
|||||||
const [showOnlyMedia, setShowOnlyMedia] = useState(false);
|
const [showOnlyMedia, setShowOnlyMedia] = useState(false);
|
||||||
const [tabIndex, setTabIndex] = useState(0);
|
const [tabIndex, setTabIndex] = useState(0);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// ✅ Redirection si non connecté
|
// ✅ Redirection si non connecté
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!getToken()) {
|
if (!getToken()) {
|
||||||
navigate("/admin/login");
|
navigate("/admin/login");
|
||||||
}
|
}
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
const itemsPerPage = 5;
|
const itemsPerPage = 5;
|
||||||
const pdfRef = useRef();
|
const pdfRef = useRef();
|
||||||
const pageHasMedia = (html) => /<img|<video|<audio|pluginfile\.php/.test(html);
|
const pageHasMedia = (html) =>
|
||||||
|
/<img|<video|<audio|pluginfile\.php/.test(html);
|
||||||
|
|
||||||
|
const generateAccessLink = (courseId, pageId) => {
|
||||||
|
const expiry = Date.now() + 24 * 60 * 60 * 1000;
|
||||||
|
const token = btoa(`${courseId}_${pageId}_${expiry}`);
|
||||||
|
const link = `${window.location.origin}/cours/lecture/${token}`;
|
||||||
|
navigator.clipboard.writeText(link);
|
||||||
|
alert("🔗 Lien copié : " + link);
|
||||||
|
};
|
||||||
|
|
||||||
const fetchCours = async () => {
|
const fetchCours = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -160,7 +170,9 @@ const ListeCours = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const filteredPages = pages.filter((page) => {
|
const filteredPages = pages.filter((page) => {
|
||||||
const nameMatch = page.name.toLowerCase().includes(searchQuery.toLowerCase());
|
const nameMatch = page.name
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchQuery.toLowerCase());
|
||||||
const mediaMatch = showOnlyMedia ? pageHasMedia(page.content) : true;
|
const mediaMatch = showOnlyMedia ? pageHasMedia(page.content) : true;
|
||||||
return nameMatch && mediaMatch;
|
return nameMatch && mediaMatch;
|
||||||
});
|
});
|
||||||
@@ -169,18 +181,28 @@ const ListeCours = () => {
|
|||||||
assign.name.toLowerCase().includes(assignmentQuery.toLowerCase())
|
assign.name.toLowerCase().includes(assignmentQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
const paginatedPages = filteredPages.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
|
const paginatedPages = filteredPages.slice(
|
||||||
|
(currentPage - 1) * itemsPerPage,
|
||||||
|
currentPage * itemsPerPage
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ padding: 4, fontFamily: 'Nunito, sans-serif', mt:5 }}>
|
<Box sx={{ padding: 4, fontFamily: "Nunito, sans-serif", mt: 5 }}>
|
||||||
<Typography variant="h4" gutterBottom align="center">Cours</Typography>
|
<Typography variant="h4" gutterBottom align="center">
|
||||||
|
Cours
|
||||||
|
</Typography>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Box sx={{ textAlign: "center" }}><CircularProgress /></Box>
|
<Box sx={{ textAlign: "center" }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
{cours.map((cours) => (
|
{cours.map((cours) => (
|
||||||
<Grid item xs={12} md={6} key={cours.id}>
|
<Grid item xs={12} md={6} key={cours.id}>
|
||||||
<Card sx={{ cursor: "pointer" }} onClick={() => handleSelect(cours)}>
|
<Card
|
||||||
|
sx={{ cursor: "pointer" }}
|
||||||
|
onClick={() => handleSelect(cours)}
|
||||||
|
>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h6">{cours.fullname}</Typography>
|
<Typography variant="h6">{cours.fullname}</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -190,35 +212,116 @@ const ListeCours = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Modal open={!!selected} onClose={() => { setSelected(null); setPages([]); }} closeAfterTransition BackdropComponent={Backdrop} BackdropProps={{ timeout: 300 }}>
|
<Modal
|
||||||
|
open={!!selected}
|
||||||
|
onClose={() => {
|
||||||
|
setSelected(null);
|
||||||
|
setPages([]);
|
||||||
|
}}
|
||||||
|
closeAfterTransition
|
||||||
|
BackdropComponent={Backdrop}
|
||||||
|
BackdropProps={{ timeout: 300 }}
|
||||||
|
>
|
||||||
<Fade in={!!selected}>
|
<Fade in={!!selected}>
|
||||||
<Box sx={{ position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", bgcolor: "background.paper", p: 4, width: "90%", maxWidth: 900, borderRadius: 2, boxShadow: 24, maxHeight: "90vh", overflowY: "auto", fontFamily: 'Nunito, sans-serif' }}>
|
<Box
|
||||||
<Typography variant="h5" gutterBottom>{selected?.fullname}</Typography>
|
sx={{
|
||||||
<Tabs value={tabIndex} onChange={(e, newValue) => setTabIndex(newValue)} centered>
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
p: 4,
|
||||||
|
width: "90%",
|
||||||
|
maxWidth: 900,
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: 24,
|
||||||
|
maxHeight: "90vh",
|
||||||
|
overflowY: "auto",
|
||||||
|
fontFamily: "Nunito, sans-serif",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
{selected?.fullname}
|
||||||
|
</Typography>
|
||||||
|
<Tabs
|
||||||
|
value={tabIndex}
|
||||||
|
onChange={(e, newValue) => setTabIndex(newValue)}
|
||||||
|
centered
|
||||||
|
>
|
||||||
<Tab label="Pages" />
|
<Tab label="Pages" />
|
||||||
<Tab label="Devoirs" />
|
<Tab label="Devoirs" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{tabIndex === 0 ? (
|
{tabIndex === 0 ? (
|
||||||
<>
|
<>
|
||||||
<Autocomplete options={pages.map((p) => p.name)} freeSolo onInputChange={(event, value) => setSearchQuery(value)} renderInput={(params) => (<TextField {...params} label="Rechercher une page" sx={{ mb: 2 }} />)} />
|
<Autocomplete
|
||||||
{loadingPages ? <CircularProgress /> : filteredPages.length > 0 ? (
|
options={pages.map((p) => p.name)}
|
||||||
|
freeSolo
|
||||||
|
onInputChange={(event, value) => setSearchQuery(value)}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Rechercher une page"
|
||||||
|
sx={{ mb: 2 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{loadingPages ? (
|
||||||
|
<CircularProgress />
|
||||||
|
) : filteredPages.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{paginatedPages.map((page) => (
|
{paginatedPages.map((page) => (
|
||||||
<Box key={page.id} sx={{ mb: 4 }}>
|
<Box key={page.id} sx={{ mb: 4 }}>
|
||||||
<Typography variant="h6" sx={{ mb: 1 }}>{page.name}</Typography>
|
<Typography variant="h6" sx={{ mb: 1 }}>
|
||||||
<Typography variant="body2" sx={{ whiteSpace: "pre-line" }} dangerouslySetInnerHTML={{ __html: page.content }} />
|
{page.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{ whiteSpace: "pre-line" }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: page.content }}
|
||||||
|
/>
|
||||||
<Box sx={{ display: "flex", gap: 1, mt: 1 }}>
|
<Box sx={{ display: "flex", gap: 1, mt: 1 }}>
|
||||||
<Button size="small" variant="outlined" onClick={() => setSelectedPage(page)}>Visualiser</Button>
|
<Button
|
||||||
<Button size="small" variant="contained" onClick={() => exportPagePDF(page)}>Exporter en PDF</Button>
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setSelectedPage(page)}
|
||||||
|
>
|
||||||
|
Visualiser
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => exportPagePDF(page)}
|
||||||
|
>
|
||||||
|
Exporter en PDF
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
color="success"
|
||||||
|
onClick={() =>
|
||||||
|
generateAccessLink(selected.id, page.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Générer lien public
|
||||||
|
</Button>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<Divider sx={{ mt: 3 }} />
|
<Divider sx={{ mt: 3 }} />
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<Box display="flex" justifyContent="center" mt={2}>
|
<Box display="flex" justifyContent="center" mt={2}>
|
||||||
<Pagination count={Math.ceil(filteredPages.length / itemsPerPage)} page={currentPage} onChange={(e, value) => setCurrentPage(value)} color="primary" />
|
<Pagination
|
||||||
|
count={Math.ceil(filteredPages.length / itemsPerPage)}
|
||||||
|
page={currentPage}
|
||||||
|
onChange={(e, value) => setCurrentPage(value)}
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
) : (<Typography>Aucune page trouvée pour ce cours.</Typography>)}
|
) : (
|
||||||
|
<Typography>Aucune page trouvée pour ce cours.</Typography>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -227,34 +330,92 @@ const ListeCours = () => {
|
|||||||
freeSolo
|
freeSolo
|
||||||
onInputChange={(event, value) => setAssignmentQuery(value)}
|
onInputChange={(event, value) => setAssignmentQuery(value)}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField {...params} label="Rechercher un devoir" sx={{ mb: 3 }} />
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Rechercher un devoir"
|
||||||
|
sx={{ mb: 3 }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{filteredAssignments.length > 0 ? filteredAssignments.map((assign) => (
|
{filteredAssignments.length > 0 ? (
|
||||||
<Box key={assign.id} sx={{ mb: 3 }}>
|
filteredAssignments.map((assign) => (
|
||||||
<Typography variant="h6">{assign.name}</Typography>
|
<Box key={assign.id} sx={{ mb: 3 }}>
|
||||||
<Typography variant="body2" sx={{ mb: 1 }} dangerouslySetInnerHTML={{ __html: assign.intro || "" }} />
|
<Typography variant="h6">{assign.name}</Typography>
|
||||||
<Button size="small" variant="outlined" onClick={() => setSelectedAssignment(assign)}>Visualiser</Button>
|
<Typography
|
||||||
<Divider sx={{ mt: 2 }} />
|
variant="body2"
|
||||||
</Box>
|
sx={{ mb: 1 }}
|
||||||
)) : (<Typography>Aucun devoir trouvé pour ce cours.</Typography>)}
|
dangerouslySetInnerHTML={{ __html: assign.intro || "" }}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setSelectedAssignment(assign)}
|
||||||
|
>
|
||||||
|
Visualiser
|
||||||
|
</Button>
|
||||||
|
<Divider sx={{ mt: 2 }} />
|
||||||
|
</Box>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Typography>Aucun devoir trouvé pour ce cours.</Typography>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Box sx={{ mt: 3, textAlign: "right" }}>
|
<Box sx={{ mt: 3, textAlign: "right" }}>
|
||||||
<Button variant="outlined" onClick={() => setSelected(null)}>Fermer</Button>
|
<Button variant="outlined" onClick={() => setSelected(null)}>
|
||||||
|
Fermer
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal open={!!selectedPage || !!selectedAssignment} onClose={() => { setSelectedPage(null); setSelectedAssignment(null); }} closeAfterTransition BackdropComponent={Backdrop} BackdropProps={{ timeout: 300 }}>
|
<Modal
|
||||||
|
open={!!selectedPage || !!selectedAssignment}
|
||||||
|
onClose={() => {
|
||||||
|
setSelectedPage(null);
|
||||||
|
setSelectedAssignment(null);
|
||||||
|
}}
|
||||||
|
closeAfterTransition
|
||||||
|
BackdropComponent={Backdrop}
|
||||||
|
BackdropProps={{ timeout: 300 }}
|
||||||
|
>
|
||||||
<Fade in={!!selectedPage || !!selectedAssignment}>
|
<Fade in={!!selectedPage || !!selectedAssignment}>
|
||||||
<Box sx={{ position: "absolute", top: 0, left: 0, width: "100vw", height: "100vh", bgcolor: "background.paper", p: 4, overflowY: "auto", fontFamily: 'Nunito, sans-serif' }}>
|
<Box
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between", mb: 2 }}>
|
sx={{
|
||||||
<Typography variant="h6">{selectedPage?.name || selectedAssignment?.name}</Typography>
|
position: "absolute",
|
||||||
<Button onClick={() => { setSelectedPage(null); setSelectedAssignment(null); }}>Fermer</Button>
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100vw",
|
||||||
|
height: "100vh",
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
p: 4,
|
||||||
|
overflowY: "auto",
|
||||||
|
fontFamily: "Nunito, sans-serif",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{ display: "flex", justifyContent: "space-between", mb: 2 }}
|
||||||
|
>
|
||||||
|
<Typography variant="h6">
|
||||||
|
{selectedPage?.name || selectedAssignment?.name}
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedPage(null);
|
||||||
|
setSelectedAssignment(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Fermer
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ maxWidth: "900px", margin: "0 auto", paddingBottom: 8 }} dangerouslySetInnerHTML={{ __html: selectedPage?.content || selectedAssignment?.intro || "" }} />
|
<Box
|
||||||
|
sx={{ maxWidth: "900px", margin: "0 auto", paddingBottom: 8 }}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html:
|
||||||
|
selectedPage?.content || selectedAssignment?.intro || "",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -262,4 +423,4 @@ const ListeCours = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListeCours;
|
export default ListeCours;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
component="footer"
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#0e467f",
|
backgroundColor: "#0e467f",
|
||||||
color: "white",
|
color: "white",
|
||||||
@@ -35,21 +36,21 @@ const Footer = () => {
|
|||||||
component="h2"
|
component="h2"
|
||||||
sx={{ fontWeight: "bold", textAlign: { xs: "center", md: "left" } }}
|
sx={{ fontWeight: "bold", textAlign: { xs: "center", md: "left" } }}
|
||||||
>
|
>
|
||||||
IN3 Intégrale Ingénierie Internationale
|
Octopusdesign
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{ marginTop: 1, textAlign: { xs: "center", md: "left" } }}
|
sx={{ marginTop: 1, textAlign: { xs: "center", md: "left" } }}
|
||||||
>
|
>
|
||||||
67 Boulevard Winston Churchill,
|
Route du Perrier,
|
||||||
<br />
|
<br />
|
||||||
72100 Le Mans, France
|
85270 - Saint Hilaire de Riez
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{ marginTop: 1, textAlign: { xs: "center", md: "left" } }}
|
sx={{ marginTop: 1, textAlign: { xs: "center", md: "left" } }}
|
||||||
>
|
>
|
||||||
<strong>Téléphone :</strong> 02.43.85.09.01
|
<strong>Téléphone :</strong> 06.00.00.00.00
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
@@ -57,11 +58,11 @@ const Footer = () => {
|
|||||||
>
|
>
|
||||||
<strong>Email :</strong>{" "}
|
<strong>Email :</strong>{" "}
|
||||||
<Link
|
<Link
|
||||||
href="mailto:contact@be-in3.com"
|
// href="mailto:contact@be-in3.com"
|
||||||
underline="hover"
|
underline="hover"
|
||||||
sx={{ color: "white" }}
|
sx={{ color: "white" }}
|
||||||
>
|
>
|
||||||
contact@be-in3.com
|
contact@octopusdesign.fr
|
||||||
</Link>
|
</Link>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -192,7 +193,7 @@ const Footer = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
© {new Date().getFullYear()} IN3 Intégrale Ingénierie Internationale.
|
© {new Date().getFullYear()} Octopusdesign.
|
||||||
Tous droits réservés.
|
Tous droits réservés.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import EditPageACF from "./components/Pages/EditPageACF";
|
|||||||
// Ajoutez les autres pages Admin
|
// Ajoutez les autres pages Admin
|
||||||
import GestionCours from "./components/Admin/GestionCours";
|
import GestionCours from "./components/Admin/GestionCours";
|
||||||
import ListeCours from "./components/Admin/ListeCours";
|
import ListeCours from "./components/Admin/ListeCours";
|
||||||
|
import CoursLecture from './components/Admin/CoursLecture';
|
||||||
|
|
||||||
// Lazy loading des pages
|
// Lazy loading des pages
|
||||||
const Home = lazy(() => import("./components/Pages/Home.jsx"));
|
const Home = lazy(() => import("./components/Pages/Home.jsx"));
|
||||||
@@ -80,6 +81,7 @@ function YourApp() {
|
|||||||
<Route path="/admin/edit-page/:id" element={<EditPageACF />} />
|
<Route path="/admin/edit-page/:id" element={<EditPageACF />} />
|
||||||
<Route path="/admin/gestion-cours" element={<GestionCours />} />
|
<Route path="/admin/gestion-cours" element={<GestionCours />} />
|
||||||
<Route path="/admin/liste-cours" element={<ListeCours />} />
|
<Route path="/admin/liste-cours" element={<ListeCours />} />
|
||||||
|
<Route path="/cours/lecture/:token" element={<CoursLecture />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
+4949
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user