The last one before the soutenance

This commit is contained in:
Lensors 2025-05-19 16:58:17 +02:00
parent 8d07c38d32
commit 25e19ec12e
10 changed files with 243 additions and 53 deletions

View File

@ -10,6 +10,7 @@ import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@WebServlet("/AfficherPage")
@ -35,6 +36,10 @@ public class AfficherPage extends HttpServlet {
request.setAttribute("listeMessages", listeMessages);
request.setAttribute("listeUtilisateurs", listeUtilisateurs);
String menuHtml = genererMenuHTML(u.getId());
request.setAttribute("menuHtml", menuHtml);
if (idStr != null ) {
try {
int id = Integer.parseInt(idStr);
@ -66,5 +71,42 @@ public class AfficherPage extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
public static String genererMenuHTML(int idU) {
Map<Integer, List<Page>> arbre = Page.getArbreDesPages(idU);
StringBuilder html = new StringBuilder();
html.append("<ul>\n");
construireHTMLRecursif(arbre, -1, html, 6);
html.append(" </ul>\n");
return html.toString();
}
private static void construireHTMLRecursif(Map<Integer, List<Page>> arbre, int parentId, StringBuilder html, int indentLevel) {
List<Page> enfants = arbre.get(parentId);
if (enfants == null || enfants.isEmpty()) return;
String indent = "\t".repeat(indentLevel);
for (Page p : enfants) {
List<Page> sousPages = arbre.get(p.getId());
boolean aSousPages = sousPages != null && !sousPages.isEmpty();
html.append(indent).append("<li>\n");
html.append(indent).append("\t<div class='is-flex is-align-items-center is-justify-content-space-between'>\n");
html.append(indent).append("\t\t<a href='AfficherPage?id=").append(p.getId()).append("'>")
.append(p.getTitre()).append("</a>\n");
html.append(indent).append("\t\t<button class='delete delete-page-btn has-background-danger ml-2' data-id='")
.append(p.getId()).append("'></button>\n");
html.append(indent).append("\t</div>\n");
if (aSousPages) {
html.append(indent).append("\t<ul class='ml-4'>\n");
construireHTMLRecursif(arbre, p.getId(), html, indentLevel + 1);
html.append(indent).append("\t</ul>\n");
}
html.append(indent).append("</li>\n");
}
}
}

View File

@ -9,7 +9,9 @@ import java.sql.Statement;
import java.time.LocalDate;
import java.sql.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import projet.Bloc.Type;
@ -283,4 +285,57 @@ public class Page extends ParamBD {
e.printStackTrace();
}
}
public static Map<Integer, List<Page>> getArbreDesPages(int idU) {
Map<Integer, List<Page>> arbre = new HashMap<>();
chargerSousPagesRecursivement(idU, arbre, null);
return arbre;
}
public static void chargerSousPagesRecursivement(int idU, Map<Integer, List<Page>> arbre, Integer idParent) {
List<Page> enfants = new ArrayList<>();
try {
Connection connexion = DriverManager.getConnection(bdURL, bdLogin, bdPassword);
String sql;
PreparedStatement pst;
if(idParent == null) {
sql = " SELECT id, titre"
+ " FROM page "
+ "WHERE auteur_id = ? AND page_parent_id IS NULL"
+ ";";
pst = connexion.prepareStatement(sql);
pst.setInt(1, idU);
} else {
sql = " SELECT id, titre"
+ " FROM page "
+ "WHERE auteur_id = ? AND page_parent_id = ?"
+ ";";
pst = connexion.prepareStatement(sql);
pst.setInt(1, idU);
pst.setInt(2, idParent);
}
ResultSet rs = pst.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String titre = rs.getString("titre");
Page page = new Page(id, idU, titre);
enfants.add(page);
}
rs.close();
connexion.close();
} catch (SQLException e) {
e.printStackTrace();
}
int key = (idParent == null) ? -1 : idParent;
arbre.put(key, enfants);
for (Page enfant : enfants) {
chargerSousPagesRecursivement(idU, arbre, enfant.getId());
}
}
}

View File

@ -7,15 +7,15 @@
</jsp:include>
<div class="columns">
<div class="columns is-flex" style="transition: all 0.3s ease;">
<!-- La colonne pour le menu des pages -->
<div class="column is-one-fifth">
<div class="column is-one-fifth" id="menuColumn">
<jsp:include page="MenuPages.jsp" />
</div>
<!-- La colonne pour la page choisie -->
<div class="column is-half">
<div class="column flex-main" id="mainColumn">
<c:choose>
<c:when test="${not empty page.titre}">
<div class="is-flex is-justify-content-space-between is-align-items mb-4">
@ -53,7 +53,7 @@
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu3" role="menu">
<div class="dropdown-menu" id="dropdown-menu4" role="menu">
<div class="dropdown-content">
<c:forEach var="u" items="${listeUtilisateurs}">
<c:if test="${u.id != utilisateur.id}">
@ -67,7 +67,7 @@
</div>
<div class="block" id="md">
<c:forEach var="bloc" items="${page.listeBlocs}">
<div class="field is-grouped is-align-items-flex-start bloc-container" draggable="true">
<div class="field is-grouped is-align-items-flex-start bloc-container hover-bloc" draggable="true">
<div class="control is-expanded">
<div
class="is-primary editor"
@ -139,6 +139,7 @@
<li><code>`texte`</code> → <code>&lt;code&gt;</code> : monospaced/code</li>
<li><code>**gras**</code> → <strong>gras</strong></li>
<li><code>*italique*</code> → <em>italique</em></li>
<li><code>$math$</code> → active le rendu mathématique en ligne (MathJax)</li>
<li><code>$$math$$ </code> → active le rendu mathématique (MathJax)</li>
</ul>
</div>
@ -174,10 +175,10 @@
</div>
<!-- La colonne pour le Tchat -->
<div class="column is-one-quarter">
<div class="column flex-tchat slide-out" id="tchatColumn">
<jsp:include page="Tchat.jsp" />
</div>
</div>
<button class="button is-small is-info vertical-toggle" id="toggleTchatBtn">Afficher / Cacher le Tchat</button>
<jsp:include page="Footer.jsp" />

View File

@ -8,15 +8,23 @@
<link href="bulma.css" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<script src="https://kit.fontawesome.com/39474be7e2.js" crossorigin="anonymous"></script>
<script type="text/javascript" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">
<script>
window.MathJax = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']]
},
svg: {
fontCache: 'global'
}
};
</script>
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
</head>
<body>
<nav class="navbar has-shadow is-white" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="/Projet/">
<h1 class="title is-2">Prise de notes collaborative</h1>
<h1 class="title is-2 has-text-success">Prise de notes collaborative</h1>
</a>
</div>
<div class="navbar-menu">

View File

@ -4,15 +4,9 @@
<h2 class="block">Menu des pages</h2>
<aside class="menu">
<ul class="menu-list" id="menuPages">
<c:forEach var="page" items="${listePages}">
<li>
<button class="delete delete-page-btn is-pulled-right has-background-danger" data-id="${page.id}"></button>
<a href="AfficherPage?id=${page.id}">${page.titre}</a>
</li>
</c:forEach>
</ul>
<div>
${menuHtml}
</div>
<hr>
<ul class="menu-list">
<c:forEach var="pagePartagees" items="${listePagesPartagees}">
@ -34,4 +28,4 @@
</form>
</li>
</ul>
</aside>
</aside>

View File

@ -20,13 +20,19 @@ export function initBlocs() {
});
});
document.querySelectorAll('.delete-bloc-btn').forEach(t => {
addDeleteBloc(t);
document.querySelectorAll('.delete-bloc-btn').forEach(btn => {
addDeleteBloc(btn);
btn.addEventListener('mouseenter', () => {
btn.closest('.hover-bloc').classList.add('hovered');
});
btn.addEventListener('mouseleave', () => {
btn.closest('.hover-bloc').classList.remove('hovered');
});
});
formatEditorContent();
document.querySelectorAll('#md [contenteditable="true"]').forEach(bloc => {
document.querySelectorAll('#md ').forEach(bloc => {
autoResize(bloc);
});
@ -169,9 +175,9 @@ export function addDeleteBloc(button) {
button.addEventListener('click', function() {
const blocId = button.dataset.id;
const blocContainer = button.closest('.bloc-container');
const bloc = blocContainer.querySelector('[contenteditable="true"]');
const bloc = blocContainer.querySelector('.editor');
if (bloc.dataset.type != "SEPARATEUR" && bloc.innerText === "") {
if (bloc.dataset.type != "SEPARATEUR" && bloc.innerText.trim() === "") {
return;
}
@ -203,7 +209,10 @@ export function addDeleteBloc(button) {
}
export function ajouterBlocVideSiBesoin() {
const allBlocs = document.querySelectorAll('#md [contenteditable="true"]');
const container = document.querySelector('#md');
if (!container) return;
const allBlocs = container.querySelectorAll('.editor');
const pageId = new URLSearchParams(window.location.search).get("id");
if (pageId === null) return;
@ -212,7 +221,7 @@ export function ajouterBlocVideSiBesoin() {
return;
}
if (allBlocs.length === 0 || allBlocs[allBlocs.length - 1].innerText.trim() !== "") {
if (allBlocs.length === 0 || allBlocs[allBlocs.length - 1].innerText.trim() !== "" || allBlocs[allBlocs.length - 1].dataset.type === "SEPARATEUR") {
const params = new URLSearchParams();
params.append("contenu", ""); // Bloc vide
params.append("type", "TEXTE"); // Type par défaut : TEXTE
@ -227,30 +236,30 @@ export function ajouterBlocVideSiBesoin() {
},
body: params.toString()
}).then(response => response.json())
.then(data => {
if (data && data.idGenere) {
const idGenere = data.idGenere;
const newBloc = creerBlocDOM({
blocId: idGenere,
content: "",
type: "TEXTE",
metadata: "{}",
ordre: allBlocs.length
});
newBloc.focus();
// Envoi d'une notification d'un nouveau bloc créé via websocket
window.socketBloc.send(JSON.stringify({
action: "newBloc",
blocId: idGenere,
content: "",
type: "TEXTE",
metadata: "{}",
ordre: allBlocs.length
}));
}
});
.then(data => {
if (data && data.idGenere) {
const idGenere = data.idGenere;
const newBloc = creerBlocDOM({
blocId: idGenere,
content: "",
type: "TEXTE",
metadata: "{}",
ordre: allBlocs.length
});
newBloc.focus();
// Envoi d'une notification d'un nouveau bloc créé via websocket
window.socketBloc.send(JSON.stringify({
action: "newBloc",
blocId: idGenere,
content: "",
type: "TEXTE",
metadata: "{}",
ordre: allBlocs.length
}));
}
});
}
}

View File

@ -73,6 +73,7 @@ export function renderBlocStyle(bloc) {
case 'PAGE':
renderPage(bloc);
bloc.classList.add('is-page-link');
bloc.contentEditable = "false";
break;
case 'SEPARATEUR':
@ -183,7 +184,7 @@ export function handleSlashCommand(bloc, texte) {
case "hr":
applyBlocType(bloc, "SEPARATEUR", {}); // ou un type adapté selon ta logique
bloc.innerText = ' ';
bloc.textContent = '';
autoResize(bloc);
resolve();
break;

View File

@ -1,5 +1,5 @@
import { initBlocs, ajouterBlocVideSiBesoin, focusDernierBloc } from './bloc.js';
import { initTchat } from './tchat.js';
import { initTchat, toggleTchat } from './tchat.js';
import { initPages } from './page.js';
import { initSocketBloc } from './socket-bloc.js';
import { initDragAndDrop } from './drag-and-drop.js';
@ -22,4 +22,23 @@ window.addEventListener('DOMContentLoaded', () => {
window.location.href = url; // Rediriger manuellement
}
});
document.querySelectorAll('.item-header .toggle').forEach(toggle => {
toggle.addEventListener('click', function (e) {
// Empêche le lien de se propager si cliqué
e.stopPropagation();
const li = this.closest('li');
const sublist = li.querySelector('ul');
if (sublist) {
sublist.classList.toggle('hidden');
this.classList.toggle('rotated');
}
});
});
document.getElementById('toggleTchatBtn').addEventListener('click', toggleTchat);
});

View File

@ -22,3 +22,22 @@ export function initTchat() {
});
}
}
export function toggleTchat() {
const tchat = document.getElementById('tchatColumn');
const main = document.getElementById('mainColumn');
if (tchat.classList.contains('slide-in')) {
tchat.classList.remove('slide-in');
tchat.classList.add('slide-out');
main.classList.remove('collapsed');
main.classList.add('expanded');
} else {
tchat.classList.remove('slide-out');
tchat.classList.add('slide-in');
main.classList.remove('expanded');
main.classList.add('collapsed');
}
}

View File

@ -93,3 +93,45 @@
.dragging {
opacity: 0.5;
}
/* Animation pour glisser la colonne vers la droite */
.slide-out {
transform: translateX(100%);
opacity: 0;
transition: transform 0.3s ease, opacity 0.3s ease;
}
/* Animation pour la faire apparaître */
.slide-in {
transform: translateX(0%);
opacity: 1;
transition: transform 0.3s ease, opacity 0.3s ease;
}
.flex-main {
flex-grow: 4;
transition: flex-grow 0.3s ease;
}
.expanded {
flex-grow: 4;
}
.collapsed {
flex-grow: 2;
}
.vertical-toggle {
position: fixed;
top: 50%;
right: 0;
transform: rotate(-90deg) translateY(-50%);
transform-origin: right center;
z-index: 999;
border-radius: 6px 6px 0 0;
}
.hover-bloc.hovered {
background-color: #ffe6e6;
transition: background-color 0.2s ease;
}