Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
VirtualKeyboardSample
/
Chart burndown
:
table0021_split_0022_light_007.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php function getWorkingDays($startDate, $endDate, $holidays = []) { $start = strtotime($startDate); $end = strtotime($endDate); $workingDays = []; setlocale(LC_TIME, 'fr_FR.UTF-8'); for ($currentDate = $start; $currentDate <= $end; $currentDate = strtotime("+1 day", $currentDate)) { $dayOfWeek = date("N", $currentDate); if ($dayOfWeek < 6 && !in_array(date("Y-m-d", $currentDate), $holidays)) { $workingDays[] = [ 'day' => date("d", $currentDate), 'day_of_week_initial' => strtoupper(strftime("%A", $currentDate)[0]), 'week_number' => date("W", $currentDate), 'full_date' => date("Y-m-d", $currentDate) // Ajout de la date complète ]; } } return $workingDays; } function getFrenchHolidays($year) { $easterDate = easter_date($year); $holidays = []; // Jours fériés fixes $holidays[] = "$year-01-01"; // Jour de l'An $holidays[] = "$year-05-01"; // Fête du Travail $holidays[] = "$year-05-08"; // Victoire 1945 $holidays[] = "$year-07-14"; // Fête Nationale $holidays[] = "$year-08-15"; // Assomption $holidays[] = "$year-11-01"; // Toussaint $holidays[] = "$year-11-11"; // Armistice $holidays[] = "$year-12-25"; // Noël // Jours fériés variables (calculés à partir de Pâques) $easter = new DateTime("@$easterDate"); $easter->setTimezone(new DateTimeZone(date_default_timezone_get())); $holidays[] = $easter->format("Y-m-d"); // Pâques (non férié officiellement mais utile) $holidays[] = $easter->modify('+1 day')->format("Y-m-d"); // Lundi de Pâques $holidays[] = $easter->modify('+38 days')->format("Y-m-d"); // Ascension $holidays[] = $easter->modify('+11 days')->format("Y-m-d"); // Lundi de Pentecôte return $holidays; } $year = 2025; $holidays = []; $holidays = getFrenchHolidays($year); // Liste des jours fériés et fêtes religieuses à exclure (exemple pour la France, à compléter avec d'autres fêtes) $old_holidays = [ "2025-01-01", // Jour de l'An "2025-04-14", // Lundi de Pâques "2025-05-01", // Fête du Travail "2025-05-08", // Victoire 1945 "2025-07-14", // Bastille "2025-08-15", // Assomption "2025-11-01", // Toussaint "2025-12-25", // Noël "2026-01-01", // Jour de l'An "2026-04-06", // Lundi de Pâques // Ajouter d'autres jours fériés ici... ]; // Définir la période de début et de fin $startDate = "2025-01-10"; $endDate = "2026-02-14"; // Appeler la fonction pour obtenir les jours ouvrés entre les deux dates $workingDays = getWorkingDays($startDate, $endDate, $holidays); ?> <!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Tableau Sticky - 3 Lignes Fixes</title> <style> .table-wrapper { width: 100%; height: calc(100vh - 100px); overflow: auto; border: 1px solid #ccc; position: relative; } table { border-collapse: collapse; width: max-content; table-layout: fixed; } th, td { padding: 3px; text-align: center; height: 40px; border: 1px solid #ccc; white-space: nowrap; min-width: 120px; position: relative; background-color: white; box-sizing: border-box; } th { background-color: #f2f2f2; position: sticky; top: 0; z-index: 100; } th:first-child, td:first-child { position: sticky; left: 0; z-index: 101; background-color: white; } th:nth-child(2), td:nth-child(2) { position: sticky; left: 120px; z-index: 100; background-color: white; } th:nth-child(3), td:nth-child(3) { position: sticky; left: 240px; z-index: 99; background-color: white; } th:first-child::after, td:first-child::after, th:nth-child(2)::after, td:nth-child(2)::after, th:nth-child(3)::after, td:nth-child(3)::after { content: ""; position: absolute; right: 0; top: 0; width: 2px; height: 100%; background-color: #ccc; z-index: 102; } tbody tr:nth-child(odd) { background-color: #f9f9f9; } th:nth-child(n+4), td:nth-child(n+4) { min-width: 40px; max-width: 40px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .second-header th { background-color: #f2f2f2; position: sticky; top: 40px; z-index: 99; } .third-header th { background-color: #f2f2f2; position: sticky; top: 80px; z-index: 98; } .second-header th:first-child, .third-header th:first-child { left: 0; z-index: 101; background-color: white; } .second-header th:nth-child(2), .third-header th:nth-child(2) { left: 120px; z-index: 100; background-color: white; } .second-header th:nth-child(3), .third-header th:nth-child(3) { left: 240px; z-index: 99; background-color: white; } thead tr:first-child th:first-child, thead tr:first-child th:nth-child(2), thead tr:first-child th:nth-child(3) { z-index: 105; background-color: #ddd; } .second-header th:first-child, .second-header th:nth-child(2), .second-header th:nth-child(3) { z-index: 104; } .third-header th:first-child, .third-header th:nth-child(2), .third-header th:nth-child(3) { z-index: 103; } thead tr:first-child th, .second-header th, .third-header th { box-shadow: inset 0 -2px #aaa; background-clip: padding-box; padding-bottom: 2px; margin-bottom: -2px; z-index: 100; } .second-header th { top: calc(40px + 2px); margin-top: 2px; } .third-header th { top: calc(80px + 2px); margin-top: 2px; } .even-week { background-color: #ffcc99 !important; } .odd-week { background-color: #e6b3ff !important; } .fourth-header th { background-color: #f2f2f2; position: sticky; top: 120px; z-index: 97; } .fourth-header th:first-child { left: 0; z-index: 101; background-color: white; } .fourth-header th:nth-child(2) { left: 120px; z-index: 100; background-color: white; } .fourth-header th:nth-child(3) { left: 240px; z-index: 99; background-color: white; } .fourth-header th:first-child, .fourth-header th:nth-child(2), .fourth-header th:nth-child(3) { z-index: 102; } .fourth-header th { top: calc(120px + 2px); margin-top: 2px; } .fourth-header th { box-shadow: inset 0 -2px #aaa; background-clip: padding-box; padding-bottom: 2px; margin-bottom: -2px; z-index: 97; } .month-even { background-color: #99ccff !important; } .month-odd { background-color: #ff9999 !important; } .btn { margin-bottom: 20px; padding: 10px; background-color: #4CAF50; color: white; border: none; cursor: pointer; font-size: 16px; border-radius: 5px; transition: background-color 0.3s ease; } .btn:hover { background-color: #45a049; } .btn-special { background-color: #2196F3; } .btn-special:hover { background-color: #1e88e5; } .btn-special:hover { background-color: #1e88e5; } #contextMenu { position: absolute; display: none; flex-direction: column; background: white; border: 1px solid #ccc; box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); border-radius: 5px; z-index: 1000; min-width: 180px; } #contextMenu .dropdown-item { display: block; width: 100%; padding: 8px 12px; text-align: left; color: #333; cursor: pointer; } #contextMenu .dropdown-item:hover { background: #f8f9fa; } #specialContextMenu { position: absolute; display: none; flex-direction: column; background: white; border: 1px solid #ccc; box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); border-radius: 5px; z-index: 1000; min-width: 180px; } #specialContextMenu .dropdown-item { display: block; width: 100%; padding: 8px 12px; text-align: left; color: #333; cursor: pointer; } #specialContextMenu .dropdown-item:hover { background: #f8f9fa; } #colorContextMenu { position: absolute; display: none; flex-direction: column; background: white; border: 1px solid #ccc; box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); border-radius: 5px; z-index: 1000; min-width: 180px; } #colorContextMenu .dropdown-item { display: block; width: 100%; padding: 8px 12px; text-align: left; color: #333; cursor: pointer; } #colorContextMenu .dropdown-item:hover { background: #f8f9fa; } .add-button { background-color: orange; color: white; border: none; padding: 5px 10px; cursor: pointer; margin-left: 5px; } .editable-input { width: 65px; box-sizing: border-box; } .collapsed { display: none; } #jsonOutput { margin-top: 20px; padding: 10px; border: 1px solid #ccc; background-color: #f9f9f9; white-space: pre-wrap; word-wrap: break-word; } #logOutput { margin-top: 20px; padding: 10px; border: 1px solid #ccc; background-color: #f9f9f9; white-space: pre-wrap; word-wrap: break-word; } .no-click { pointer-events: none; } #debugOutput { margin-top: 20px; padding: 10px; border: 1px solid #ccc; background-color: #f9f9f9; white-space: pre-wrap; word-wrap: break-word; } </style> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> </head> <body> <div class="modal fade" id="confirmationModal" tabindex="-1" aria-labelledby="confirmationModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="confirmationModalLabel">Confirmation</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> Voulez-vous vraiment supprimer cette ligne ? </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" id="cancelDelete">Annuler</button> <button type="button" class="btn btn-danger" id="confirmDelete">OK</button> </div> </div> </div> </div> <div class="modal fade" id="successModal" tabindex="-1" aria-labelledby="successModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="successModalLabel">Succès</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> Données sauvegardées avec succès ! </div> <div class="modal-footer"> <button type="button" class="btn btn-primary" id="closeSuccessModal">OK</button> </div> </div> </div> </div> <div class="modal fade" id="messageModal" tabindex="-1" aria-labelledby="messageModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="messageModalLabel">Message</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body" id="messageModalBody"> </div> <div class="modal-footer"> <button type="button" class="btn btn-primary" data-dismiss="modal">OK</button> </div> </div> </div> </div> <div id="colorContextMenu" class="dropdown-menu" style="display: none;"> <button class="dropdown-item" style="background-color: green;" data-color="green">Vert</button> <button class="dropdown-item" style="background-color: blue;" data-color="blue">Bleu</button> <button class="dropdown-item" style="background-color: red;" data-color="red">Rouge</button> <button class="dropdown-item" style="background-color: yellow;" data-color="yellow">Jaune</button> <button class="dropdown-item" style="background-color: orange;" data-color="orange">Orange</button> <button class="dropdown-item" style="background-color: purple;" data-color="purple">Violet</button> <button class="dropdown-item" style="background-color: pink;" data-color="pink">Rose</button> <button class="dropdown-item" style="background-color: brown;" data-color="brown">Marron</button> <button class="dropdown-item" style="background-color: cyan;" data-color="cyan">Cyan</button> <button class="dropdown-item" style="background-color: gray;" data-color="gray">Gris</button> </div> <div class="modal fade" id="rangeSelectionModal" tabindex="-1" aria-labelledby="rangeSelectionModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="rangeSelectionModalLabel">Sélection de Plage</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <div class="form-group"> <label for="startCellId">Cellule de Départ</label> <input type="text" class="form-control" id="startCellId" disabled> </div> <div class="form-group"> <label for="endCellId">Cellule de Fin</label> <input type="text" class="form-control" id="endCellId"> </div> <div class="form-group"> <label for="rangeComboBox">Sélectionnez une valeur</label> <select class="form-control" id="rangeComboBox"> </select> </div> <div id="rangeResult" class="form-group"> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" id="cancelRangeSelection">Annuler</button> <button type="button" class="btn btn-primary" id="confirmRangeSelection">Valider</button> </div> </div> </div> </div> <div class="table-wrapper"> <table> <thead> <tr> <th></th> <th>plouf</th> <th>cretin</th> <?php foreach ($workingDays as $dateData): ?> <th> <?php echo $dateData['day']; ?> <br> </th> <?php endforeach; ?> </tr> <tr class="second-header"> <th></th> <th>Valeur 1</th> <th>Valeur 2</th> <?php $currentWeek = null; $weekClass = ""; foreach ($workingDays as $dateData): if ($currentWeek !== $dateData['week_number']) { $currentWeek = $dateData['week_number']; $weekClass = ($currentWeek % 2 === 0) ? 'even-week' : 'odd-week'; } ?> <th class="<?= $weekClass; ?>"><?php echo $dateData['day_of_week_initial']; ?></th> <?php endforeach; ?> </tr> <tr class="third-header"> <th></th> <th>Info 1</th> <th>Info 2</th> <?php $currentWeek = null; $colspan = 0; foreach ($workingDays as $index => $dateData): if ($currentWeek !== $dateData['week_number']) { if ($currentWeek !== null) { $weekClass = $currentWeek % 2 === 0 ? 'even-week' : 'odd-week'; echo '<th class="' . $weekClass . '" colspan="' . $colspan . '">' . $currentWeek . '</th>'; } $currentWeek = $dateData['week_number']; $colspan = 1; } else { $colspan++; } endforeach; if ($currentWeek !== null) { $weekClass = $currentWeek % 2 === 0 ? 'even-week' : 'odd-week'; echo '<th class="' . $weekClass . '" colspan="' . $colspan . '">' . $currentWeek . '</th>'; } ?> </tr> <tr class="fourth-header"> <th></th> <th>Label 1</th> <th>Label 2</th> <?php $currentMonthYear = null; $colspan = 0; $monthClass = ""; $colorIndex = 0; $colors = ['month-even', 'month-odd']; foreach ($workingDays as $index => $dateData): $monthYear = ucfirst(strftime("%B %Y", strtotime($dateData['full_date']))); if ($currentMonthYear !== $monthYear) { if ($currentMonthYear !== null) { echo '<th class="' . $monthClass . '" colspan="' . $colspan . '">' . $currentMonthYear . '</th>'; } $currentMonthYear = $monthYear; $colspan = 1; $monthClass = $colors[$colorIndex % 2]; $colorIndex++; } else { $colspan++; } endforeach; if ($currentMonthYear !== null) { echo '<th class="' . $monthClass . '" colspan="' . $colspan . '">' . $currentMonthYear . '</th>'; } ?> </tr> </thead> <tbody> </tbody> </table> </div> <button id="addRowButton" class="btn">Ajouter une ligne</button> <button id="addSpecialRowButton" class="btn btn-special">Ajouter une ligne spéciale</button> <button id="exportButton" class="btn">EXPORT</button> <button id="importButton" class="btn">IMPORT</button> <button id="logButton" class="btn">Log</button> <button id="buildButton" class="btn">Build</button> <button id="blablaButton" class="btn">Blabla</button> <div id="generatedDataOutput"></div> <div id="jsonOutput"></div> <div id="debugOutput"></div> <script> document.addEventListener("DOMContentLoaded", function () { const tableBody = document.querySelector("tbody"); let specialRowCounter = 1; let rowCounter = 1; let linkageCounter = 1; let standardRowsData = []; let estimationRowsData = []; let specialRowsData = []; // Tableau dédié pour stocker les objets des lignes spéciales let linkages = []; const specialRowStates = {}; const associations = {}; let currentCell = null; let keydownHandled = false; let alertShown = false; const contextMenu = document.createElement("div"); contextMenu.id = "contextMenu"; contextMenu.classList.add("dropdown-menu"); contextMenu.style.display = "none"; document.body.appendChild(contextMenu); const specialContextMenu = document.createElement("div"); specialContextMenu.id = "specialContextMenu"; specialContextMenu.classList.add("dropdown-menu"); specialContextMenu.style.display = "none"; document.body.appendChild(specialContextMenu); const colorContextMenu = document.getElementById("colorContextMenu"); const colorButtons = colorContextMenu.querySelectorAll(".dropdown-item"); colorButtons.forEach(button => { button.addEventListener("click", function () { const color = this.getAttribute("data-color"); const cell = document.querySelector(".selected-cell"); if (cell) { cell.style.backgroundColor = color; cell.dataset.cellColor = color; hideColorContextMenu(); updateRowData(cell); } }); }); function generateRandomId(length = 8) { const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * characters.length)); } return result; } function addRow(afterRow = null) { const rowNumber = tableBody.rows.length + 1; const newRow = document.createElement("tr"); newRow.setAttribute("draggable", "true"); newRow.dataset.rowId = `standard-${generateRandomId()}`; const rowData = { id: newRow.dataset.rowId, name: `Row ${rowNumber}`, selectValue: "1", count: "0", cells: [] }; const firstCells = [`Row ${rowNumber}`, "", "0"]; firstCells.forEach((text, index) => { const td = document.createElement("td"); td.dataset.cellId = `cell-${rowData.id}-${index}`; if (index === 0) { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = rowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { if (!keydownHandled && !alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } keydownHandled = false; alertShown = false; }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { keydownHandled = true; if (!alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } } }); td.appendChild(input); } else if (index === 1) { const select = document.createElement("select"); for (let i = 1; i <= 10; i++) { const option = document.createElement("option"); option.value = i; option.textContent = `Option ${i}`; select.appendChild(option); } select.value = rowData.selectValue; select.addEventListener("change", function () { updateRowData(select); }); td.appendChild(select); } else { td.textContent = text; } if (index === 0) { td.classList.add("row-header"); td.addEventListener("contextmenu", function (event) { event.preventDefault(); showContextMenu(event, newRow); }); } newRow.appendChild(td); }); const headerCells = document.querySelectorAll("thead tr:nth-child(2) th:not(.weekend):not(.holiday)"); const workingDaysCount = headerCells.length; for (let i = 0; i < workingDaysCount - 3; i++) { const td = document.createElement("td"); td.dataset.cellId = `cell-${rowData.id}-${i}`; td.dataset.cellValue = "0"; td.dataset.cellColor = "white"; td.textContent = "0"; td.style.backgroundColor = "white"; td.addEventListener("click", function () { const newValue = td.dataset.cellValue === "0" ? "1" : "0"; td.dataset.cellValue = newValue; td.textContent = newValue; td.style.backgroundColor = newValue === "1" ? "green" : "white"; td.dataset.cellColor = newValue === "1" ? "green" : "white"; updateCountCell(newRow); updateRowData(td); }); td.addEventListener("contextmenu", function (event) { event.preventDefault(); if (td.dataset.cellValue === "1") { showColorContextMenu(event, td); } }); td.title = `ID: ${td.dataset.cellId}`; newRow.appendChild(td); rowData.cells.push({ value: "0", color: "white" }); } if (afterRow) { tableBody.insertBefore(newRow, afterRow.nextSibling); } else { tableBody.appendChild(newRow); } newRow.addEventListener("dragstart", dragStart); newRow.addEventListener("dragover", dragOver); newRow.addEventListener("drop", drop); rowCounter++; standardRowsData.push(rowData); const estimationRow = document.createElement("tr"); estimationRow.setAttribute("draggable", "true"); estimationRow.dataset.rowId = `estimation-${generateRandomId()}`; estimationRow.classList.add("estimation-row"); const estimationRowData = { id: estimationRow.dataset.rowId, name: `Estimation ${rowNumber}`, selectValue: "1", estimationValue: "0" }; const estimationFirstCells = [`Estimation ${rowNumber}`, "", "0"]; estimationFirstCells.forEach((text, index) => { const td = document.createElement("td"); td.dataset.cellId = `cell-${estimationRowData.id}-${index}`; if (index === 0) { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = estimationRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { if (!keydownHandled && !alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } keydownHandled = false; alertShown = false; }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { keydownHandled = true; if (!alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } } }); td.appendChild(input); } else if (index === 1) { const select = document.createElement("select"); for (let i = 1; i <= 10; i++) { const option = document.createElement("option"); option.value = i; option.textContent = `Option ${i}`; select.appendChild(option); } select.value = estimationRowData.selectValue; select.addEventListener("change", function () { updateRowData(select); }); td.appendChild(select); } else { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = "1"; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { if (!keydownHandled && !alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; const value = parseInt(input.value, 10); if (isNaN(value) || value < 1 || value > 365) { input.value = "1"; showMessageModal("La valeur doit être un nombre entier compris entre 1 et 365."); } else { updateRowData(input); } } keydownHandled = false; alertShown = false; }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { keydownHandled = true; if (!alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; const value = parseInt(input.value, 10); if (isNaN(value) || value < 1 || value > 365) { input.value = "1"; showMessageModal("La valeur doit être un nombre entier compris entre 1 et 365."); } else { updateRowData(input); } } } }); td.appendChild(input); } if (index === 0) { td.classList.add("row-header"); td.addEventListener("contextmenu", function (event) { event.preventDefault(); showContextMenu(event, estimationRow); }); } estimationRow.appendChild(td); }); const mergedCell = document.createElement("td"); mergedCell.colSpan = workingDaysCount - 3; mergedCell.textContent = ""; mergedCell.classList.add("no-click"); estimationRow.appendChild(mergedCell); if (afterRow) { tableBody.insertBefore(estimationRow, newRow.nextSibling); } else { tableBody.appendChild(estimationRow); } estimationRow.addEventListener("dragstart", dragStart); estimationRow.addEventListener("dragover", dragOver); estimationRow.addEventListener("drop", drop); const linkage = { linkageId: `linkage-${generateRandomId()}`, standardRowId: newRow.dataset.rowId, estimationRowId: estimationRow.dataset.rowId }; linkages.push(linkage); displayLinkages(); rowCounter++; estimationRowsData.push(estimationRowData); } function updateRowData(element) { const row = element.closest("tr"); const rowId = row.dataset.rowId; if (rowId.startsWith("standard-")) { let standardRowData = standardRowsData.find(data => data.id === rowId); if (!standardRowData) { standardRowData = { id: rowId, name: "", selectValue: "", count: "", cells: [] }; standardRowsData.push(standardRowData); } const cells = row.querySelectorAll("td"); standardRowData.name = cells[0].querySelector("input").value; standardRowData.selectValue = cells[1].querySelector("select").value; standardRowData.count = cells[2].textContent.trim(); standardRowData.cells = []; cells.forEach((cell, index) => { if (index > 2) { standardRowData.cells.push({ value: cell.dataset.cellValue, color: cell.dataset.cellColor }); } }); console.log(`Mise à jour de la ligne standard : ${rowId}`); } else if (rowId.startsWith("estimation-")) { let estimationRowData = estimationRowsData.find(data => data.id === rowId); if (!estimationRowData) { estimationRowData = { id: rowId, name: "", selectValue: "", estimationValue: "" }; estimationRowsData.push(estimationRowData); } const cells = row.querySelectorAll("td"); estimationRowData.name = cells[0].querySelector("input").value; estimationRowData.selectValue = cells[1].querySelector("select").value; estimationRowData.estimationValue = cells[2].querySelector("input").value; console.log(`Mise à jour de la ligne d'estimation : ${rowId}`); } else if (rowId.startsWith("special-")) { let specialRowData = specialRowsData.find(data => data.id === rowId); if (!specialRowData) { specialRowData = { id: rowId, name: "" }; specialRowsData.push(specialRowData); } specialRowData.name = row.querySelector("input").value; console.log(`Mise à jour de la ligne spéciale : ${rowId}`); } const objetTache = createObjetTache(); console.log("Objet Tache :", JSON.stringify(objetTache, null, 2)); } function displayLinkages() { const debugOutputElement = document.getElementById("debugOutput"); debugOutputElement.textContent = JSON.stringify(linkages, null, 2); } function updateCountCell(row) { const countCell = row.cells[2]; const count = Array.from(row.cells).slice(3).filter(cell => cell.dataset.cellValue === "1").length; countCell.textContent = count; } function displaySpecialRowsData() { console.log("Contenu de specialRowsData :"); specialRowsData.forEach((rowData, index) => { console.log(`Ligne spéciale ${index + 1} :`, rowData); }); } function addSpecialRow(afterRow = null) { const newRow = document.createElement("tr"); newRow.classList.add("special-row"); newRow.setAttribute("draggable", "true"); newRow.dataset.rowId = `special-${generateRandomId()}`; const specialRowData = { id: newRow.dataset.rowId, name: `Ligne spéciale ${specialRowCounter}` }; const firstCell = document.createElement("td"); firstCell.classList.add("row-header"); const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = specialRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { input.classList.remove("editing"); input.style.width = "65px"; updateRowData(input); }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { input.classList.remove("editing"); input.blur(); input.style.width = "65px"; updateRowData(input); } }); input.addEventListener("input", function () { updateRowData(input); }); const addButton = document.createElement("button"); addButton.classList.add("add-button"); addButton.textContent = "+"; addButton.addEventListener("click", function () { toggleCollapse(newRow); }); firstCell.appendChild(input); firstCell.appendChild(addButton); firstCell.addEventListener("contextmenu", function (event) { event.preventDefault(); showSpecialContextMenu(event, newRow); }); newRow.appendChild(firstCell); const mergedCell = document.createElement("td"); mergedCell.colSpan = 2; mergedCell.textContent = ""; newRow.appendChild(mergedCell); const td = document.createElement("td"); td.textContent = `Ligne spéciale ${specialRowCounter} - Détails`; td.colSpan = document.querySelector("thead tr:nth-child(2)").cells.length - 3; newRow.appendChild(td); if (afterRow) { tableBody.insertBefore(newRow, afterRow.nextSibling); } else { tableBody.appendChild(newRow); } newRow.addEventListener("dragstart", dragStart); newRow.addEventListener("dragover", dragOver); newRow.addEventListener("drop", drop); specialRowsData.push(specialRowData); // Ajouter l'objet dans le tableau dédié specialRowCounter++; // Afficher le contenu du tableau specialRowsData après l'ajout displaySpecialRowsData(); } function displayRowId(row) { const rowId = row.dataset.rowId; alert(`Identifiant de la ligne : ${rowId}`); } function toggleCollapse(specialRow) { const nextSpecialRow = getNextSpecialRow(specialRow); const rowsToToggle = []; let currentRow = specialRow.nextElementSibling; while (currentRow && currentRow !== nextSpecialRow) { if (!currentRow.classList.contains("special-row")) { rowsToToggle.push(currentRow); } currentRow = currentRow.nextElementSibling; } rowsToToggle.forEach(row => { row.classList.toggle("collapsed"); }); } function getNextSpecialRow(specialRow) { let currentRow = specialRow.nextElementSibling; while (currentRow) { if (currentRow.classList.contains("special-row")) { return currentRow; } currentRow = currentRow.nextElementSibling; } return null; } function showContextMenu(event, row) { contextMenu.innerHTML = ` <button class="dropdown-item" id="add-row">Ajouter une ligne</button> <button class="dropdown-item text-danger" id="delete-row">Supprimer cette ligne</button> <button class="dropdown-item" id="show-linkage">Afficher la liaison</button> `; contextMenu.style.top = `${event.clientY}px`; contextMenu.style.left = `${event.clientX}px`; contextMenu.style.display = "flex"; contextMenu.style.flexDirection = "column"; document.getElementById("add-row").addEventListener("click", function () { addRow(row); hideContextMenu(); }); document.getElementById("delete-row").addEventListener("click", function () { showConfirmationModal(row); hideContextMenu(); }); document.getElementById("show-linkage").addEventListener("click", function () { const linkage = linkages.find(link => link.standardRowId === row.dataset.rowId || link.estimationRowId === row.dataset.rowId); if (linkage) { alert(`Liaison trouvée :\nID: ${linkage.linkageId}\nLigne standard: ${linkage.standardRowId}\nLigne estimation: ${linkage.estimationRowId}`); } else { alert("Aucune liaison trouvée pour cette ligne."); } hideContextMenu(); }); } function showSpecialContextMenu(event, row) { specialContextMenu.innerHTML = ` <button class="dropdown-item" id="add-special-row">Ajouter une ligne spéciale</button> <button class="dropdown-item text-danger" id="delete-special-row">Supprimer cette ligne</button> <label class="dropdown-item"> <input type="checkbox" id="special-checkbox"> Attacher </label> `; specialContextMenu.style.top = `${event.clientY}px`; specialContextMenu.style.left = `${event.clientX}px`; specialContextMenu.style.display = "flex"; specialContextMenu.style.flexDirection = "column"; const checkbox = document.getElementById("special-checkbox"); const rowId = row.dataset.rowId; let hasStandardRowsBelow = false; let currentRow = row.nextElementSibling; const standardRows = []; while (currentRow && !currentRow.classList.contains("special-row")) { if (!currentRow.classList.contains("special-row")) { hasStandardRowsBelow = true; standardRows.push(currentRow); } currentRow = currentRow.nextElementSibling; } if (hasStandardRowsBelow) { checkbox.disabled = false; } else { checkbox.disabled = true; checkbox.checked = false; } if (specialRowStates[rowId]) { checkbox.checked = specialRowStates[rowId].checked; } checkbox.addEventListener("change", function () { specialRowStates[rowId] = { checked: checkbox.checked, rows: standardRows }; standardRows.forEach(standardRow => { const firstCell = standardRow.querySelector("td:first-child"); if (checkbox.checked) { firstCell.style.backgroundColor = "yellow"; standardRow.classList.add("no-drag"); } else { firstCell.style.backgroundColor = ""; standardRow.classList.remove("no-drag"); } }); if (checkbox.checked) { associations[rowId] = standardRows.map(row => row.dataset.rowId); } else { delete associations[rowId]; } console.log(`Lignes standards concernées : ${standardRows.map(row => row.rowIndex).join(", ")}`); }); document.getElementById("add-special-row").addEventListener("click", function () { addSpecialRow(row); hideSpecialContextMenu(); }); document.getElementById("delete-special-row").addEventListener("click", function () { showConfirmationModal(row); hideSpecialContextMenu(); }); } function showColorContextMenu(event, cell) { colorContextMenu.style.top = `${event.clientY}px`; colorContextMenu.style.left = `${event.clientX}px`; colorContextMenu.style.display = "flex"; colorContextMenu.style.flexDirection = "column"; document.querySelectorAll(".selected-cell").forEach(c => c.classList.remove("selected-cell")); cell.classList.add("selected-cell"); } function displayStandardRowsData() { console.log("Contenu de standardRowsData :"); standardRowsData.forEach((rowData, index) => { console.log(`Ligne ${index + 1} :`, rowData); }); } function displayEstimationRowsData() { console.log("Contenu de estimationRowsData :"); estimationRowsData.forEach((rowData, index) => { console.log(`Ligne ${index + 1} :`, rowData); }); } function displaySpecialRowsData() { console.log("Contenu de specialRowsData :"); specialRowsData.forEach((rowData, index) => { console.log(`Ligne ${index + 1} :`, rowData); }); } function showConfirmationModal(row) { const modal = new bootstrap.Modal(document.getElementById('confirmationModal')); modal.show(); document.getElementById('confirmDelete').addEventListener('click', function () { displayStandardRowsData(); displayEstimationRowsData(); displaySpecialRowsData(); linkages.forEach((rowData, index) => { console.log(` ******* ${index + 1} :`, rowData); }); // Afficher le nombre d'éléments dans le tableau linkages avant suppression console.log("Nombre d'éléments dans linkages début :", linkages.length); const rowId = row.dataset.rowId; const isStandardRow = rowId.startsWith("standard-"); const isEstimationRow = rowId.startsWith("estimation-"); const isSpecialRow = rowId.startsWith("special-"); let linkageIndex = -1; if (isStandardRow) { linkageIndex = linkages.findIndex(link => link.standardRowId === rowId); } else if (isEstimationRow) { linkageIndex = linkages.findIndex(link => link.estimationRowId === rowId); } else if (isSpecialRow) { linkageIndex = linkages.findIndex(link => link.specialRowId === rowId); } if (linkageIndex !== -1) { const linkage = linkages[linkageIndex]; console.log("Liaison trouvée :"); console.log("Index de la liaison :", linkageIndex); // Afficher l'indice de la liaison console.log("ID de la liaison :", linkage.linkageId); console.log("ID de la ligne standard :", linkage.standardRowId); console.log("ID de la ligne d'estimation :", linkage.estimationRowId); // Afficher le contenu des objets de liaison avant la suppression console.log("Objets de liaison avant suppression :", linkages); // Supprimer la liaison et les lignes associées standardRowsData = standardRowsData.filter(data => data.id !== linkage.standardRowId); estimationRowsData = estimationRowsData.filter(data => data.id !== linkage.estimationRowId); specialRowsData = specialRowsData.filter(data => data.id !== linkage.specialRowId); linkages.splice(linkageIndex, 1); console.log("Nombre d'éléments dans linkages fin :", linkages.length); // Afficher le contenu des objets de liaison après la suppression console.log("Objets de liaison après suppression :", linkages); displayStandardRowsData(); displayEstimationRowsData(); displaySpecialRowsData(); linkages.forEach((rowData, index) => { console.log(` ******* ${index + 1} :`, rowData); }); // Supprimer les lignes du DOM regenerateTableAfterDeletion_bis(); } modal.hide(); }); document.getElementById('cancelDelete').addEventListener('click', function () { modal.hide(); }); } function regenerateTableAfterDeletion_bis() { // Effacer le contenu actuel du tableau tableBody.innerHTML = ""; // Parcourir les liaisons pour recréer les lignes linkages.forEach(linkage => { // Trouver les données correspondantes pour les lignes standard et d'estimation const standardRowData = standardRowsData.find(row => row.id === linkage.standardRowId); const estimationRowData = estimationRowsData.find(row => row.id === linkage.estimationRowId); const specialRowData = specialRowsData.find(row => row.id === linkage.specialRowId); // Créer une nouvelle ligne standard const standardRow = document.createElement("tr"); standardRow.setAttribute("draggable", "true"); standardRow.dataset.rowId = standardRowData.id; // Ajouter les cellules pour la ligne standard const firstCells = [standardRowData.name, "", standardRowData.count]; firstCells.forEach((text, index) => { const td = document.createElement("td"); td.dataset.cellId = `cell-${standardRowData.id}-${index}`; if (index === 0) { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = standardRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { if (!keydownHandled && !alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } keydownHandled = false; alertShown = false; }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { keydownHandled = true; if (!alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } } }); td.appendChild(input); } else if (index === 1) { const select = document.createElement("select"); for (let i = 1; i <= 10; i++) { const option = document.createElement("option"); option.value = i; option.textContent = `Option ${i}`; select.appendChild(option); } select.value = standardRowData.selectValue; select.addEventListener("change", function () { updateRowData(select); }); td.appendChild(select); } else { td.textContent = text; } if (index === 0) { td.classList.add("row-header"); td.addEventListener("contextmenu", function (event) { event.preventDefault(); showContextMenu(event, standardRow); }); } standardRow.appendChild(td); }); // Ajouter les cellules de jours ouvrés const headerCells = document.querySelectorAll("thead tr:nth-child(2) th:not(.weekend):not(.holiday)"); const workingDaysCount = headerCells.length; for (let i = 0; i < workingDaysCount - 3; i++) { const td = document.createElement("td"); td.dataset.cellId = `cell-${standardRowData.id}-${i}`; td.dataset.cellValue = standardRowData.cells[i].value; td.dataset.cellColor = standardRowData.cells[i].color; td.textContent = standardRowData.cells[i].value; td.style.backgroundColor = standardRowData.cells[i].color; td.addEventListener("click", function () { const newValue = td.dataset.cellValue === "0" ? "1" : "0"; td.dataset.cellValue = newValue; td.textContent = newValue; td.style.backgroundColor = newValue === "1" ? "green" : "white"; td.dataset.cellColor = newValue === "1" ? "green" : "white"; updateCountCell(standardRow); updateRowData(td); }); td.addEventListener("contextmenu", function (event) { event.preventDefault(); if (td.dataset.cellValue === "1") { showColorContextMenu(event, td); } }); td.title = `ID: ${td.dataset.cellId}`; standardRow.appendChild(td); } // Ajouter la ligne standard au tableau tableBody.appendChild(standardRow); // Ajouter les événements de drag-and-drop standardRow.addEventListener("dragstart", dragStart); standardRow.addEventListener("dragover", dragOver); standardRow.addEventListener("drop", drop); // Créer une nouvelle ligne d'estimation const estimationRow = document.createElement("tr"); estimationRow.setAttribute("draggable", "true"); estimationRow.dataset.rowId = estimationRowData.id; estimationRow.classList.add("estimation-row"); // Ajouter les cellules pour la ligne d'estimation const estimationFirstCells = [estimationRowData.name, "", estimationRowData.estimationValue]; estimationFirstCells.forEach((text, index) => { const td = document.createElement("td"); td.dataset.cellId = `cell-${estimationRowData.id}-${index}`; if (index === 0) { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = estimationRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { if (!keydownHandled && !alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } keydownHandled = false; alertShown = false; }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { keydownHandled = true; if (!alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; console.clear; updateRowData(input); } } }); td.appendChild(input); } else if (index === 1) { const select = document.createElement("select"); for (let i = 1; i <= 10; i++) { const option = document.createElement("option"); option.value = i; option.textContent = `Option ${i}`; select.appendChild(option); } select.value = estimationRowData.selectValue; select.addEventListener("change", function () { updateRowData(select); }); td.appendChild(select); } else { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = text; input.addEventListener("blur", function () { const value = parseInt(input.value, 10); if (isNaN(value) || value < 1 || value > 365) { input.value = "1"; showMessageModal("La valeur doit être un nombre entier compris entre 1 et 365."); } else { updateRowData(input); } }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { const value = parseInt(input.value, 10); if (isNaN(value) || value < 1 || value > 365) { input.value = "1"; showMessageModal("La valeur doit être un nombre entier compris entre 1 et 365."); } else { updateRowData(input); } } }); td.appendChild(input); } if (index === 0) { td.classList.add("row-header"); td.addEventListener("contextmenu", function (event) { event.preventDefault(); showContextMenu(event, estimationRow); }); } estimationRow.appendChild(td); }); // Ajouter une cellule fusionnée pour la ligne d'estimation const mergedCell = document.createElement("td"); mergedCell.colSpan = workingDaysCount - 3; mergedCell.textContent = ""; mergedCell.classList.add("no-click"); estimationRow.appendChild(mergedCell); // Ajouter la ligne d'estimation au tableau tableBody.appendChild(estimationRow); // Ajouter les événements de drag-and-drop estimationRow.addEventListener("dragstart", dragStart); estimationRow.addEventListener("dragover", dragOver); estimationRow.addEventListener("drop", drop); // Créer une nouvelle ligne spéciale if (specialRowData) { const specialRow = document.createElement("tr"); specialRow.classList.add("special-row"); specialRow.setAttribute("draggable", "true"); specialRow.dataset.rowId = specialRowData.id; const firstCell = document.createElement("td"); firstCell.classList.add("row-header"); const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = specialRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { input.classList.remove("editing"); input.style.width = "65px"; updateRowData(input); }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { input.classList.remove("editing"); input.blur(); input.style.width = "65px"; updateRowData(input); } }); input.addEventListener("input", function () { updateRowData(input); }); const addButton = document.createElement("button"); addButton.classList.add("add-button"); addButton.textContent = "+"; addButton.addEventListener("click", function () { toggleCollapse(specialRow); }); firstCell.appendChild(input); firstCell.appendChild(addButton); firstCell.addEventListener("contextmenu", function (event) { event.preventDefault(); showSpecialContextMenu(event, specialRow); }); specialRow.appendChild(firstCell); const mergedCell = document.createElement("td"); mergedCell.colSpan = 2; mergedCell.textContent = ""; specialRow.appendChild(mergedCell); const td = document.createElement("td"); td.textContent = `${specialRowData.name} - Détails`; td.colSpan = document.querySelector("thead tr:nth-child(2)").cells.length - 3; specialRow.appendChild(td); tableBody.appendChild(specialRow); specialRow.addEventListener("dragstart", dragStart); specialRow.addEventListener("dragover", dragOver); specialRow.addEventListener("drop", drop); } }); // Afficher les liaisons dans la console displayLinkages(); } function showMessageModal(message) { const modal = new bootstrap.Modal(document.getElementById('messageModal')); const modalBody = document.getElementById('messageModalBody'); modalBody.textContent = message; modal.show(); } document.addEventListener("click", function (event) { if (!contextMenu.contains(event.target)) { hideContextMenu(); } if (!specialContextMenu.contains(event.target)) { hideSpecialContextMenu(); } if (!colorContextMenu.contains(event.target)) { hideColorContextMenu(); } const inputs = document.querySelectorAll(".editable-input"); inputs.forEach(input => { if (!input.contains(event.target)) { input.classList.remove("editing"); } }); }); document.addEventListener("keydown", function (event) { if (event.key === "Escape") { hideContextMenu(); hideSpecialContextMenu(); hideColorContextMenu(); } }); function hideContextMenu() { contextMenu.style.display = "none"; } function hideSpecialContextMenu() { specialContextMenu.style.display = "none"; } function hideColorContextMenu() { colorContextMenu.style.display = "none"; } document.getElementById("addRowButton").addEventListener("click", addRow); document.getElementById("addSpecialRowButton").addEventListener("click", addSpecialRow); document.querySelectorAll("tbody tr td:first-child").forEach(td => { td.addEventListener("contextmenu", function (event) { event.preventDefault(); showContextMenu(event, td.parentElement); }); }); function dragStart(event) { const row = event.target.closest("tr"); if (row.classList.contains("no-drag")) { event.preventDefault(); return; } event.dataTransfer.setData("text/plain", row.rowIndex); row.classList.add("dragging"); } function dragOver(event) { event.preventDefault(); const draggingRow = document.querySelector(".dragging"); const targetRow = event.target.closest("tr"); if (targetRow && draggingRow !== targetRow && !targetRow.classList.contains("no-drag")) { const rect = targetRow.getBoundingClientRect(); const offset = rect.top + (rect.height / 2); if (event.clientY - offset > 0) { tableBody.insertBefore(draggingRow, targetRow.nextSibling); } else { tableBody.insertBefore(draggingRow, targetRow); } } } function drop(event) { event.preventDefault(); const draggingRow = document.querySelector(".dragging"); draggingRow.classList.remove("dragging"); if (draggingRow.classList.contains("special-row")) { const specialRowId = draggingRow.dataset.rowId; if (associations[specialRowId]) { const associatedRowIds = associations[specialRowId]; associatedRowIds.forEach(rowId => { const associatedRow = document.querySelector(`tr[data-row-id="${rowId}"]`); if (associatedRow) { tableBody.insertBefore(associatedRow, draggingRow.nextSibling); } }); } } } function traceTable() { const tableData = []; const rows = tableBody.querySelectorAll("tr"); rows.forEach(row => { const rowData = {}; const cells = row.querySelectorAll("td"); if (row.dataset.rowId.startsWith("special-")) { const inputCell = cells[0]; const inputValue = inputCell.querySelector("input").value; rowData.id = row.dataset.rowId; rowData.name = inputValue; } else if (row.dataset.rowId.startsWith("estimation-")) { const inputCell = cells[0]; const inputValue = inputCell.querySelector("input").value; rowData.id = row.dataset.rowId; rowData.name = inputValue; rowData.estimationValue = cells[2].querySelector("input").value; rowData.selectValue = cells[1].querySelector("select").value; } else { rowData.id = row.dataset.rowId; rowData.name = cells[0].querySelector("input").value; rowData.selectValue = cells[1].querySelector("select").value; rowData.count = cells[2].textContent.trim(); rowData.cells = []; cells.forEach((cell, index) => { if (index > 2) { rowData.cells.push({ value: cell.dataset.cellValue, color: cell.dataset.cellColor }); } }); } tableData.push(rowData); }); return tableData; } function openRangeSelectionModal(cell) { const modal = new bootstrap.Modal(document.getElementById('rangeSelectionModal')); const startCellIdInput = document.getElementById('startCellId'); const endCellIdInput = document.getElementById('endCellId'); const rangeResult = document.getElementById('rangeResult'); const rangeComboBox = document.getElementById('rangeComboBox'); rangeComboBox.innerHTML = ""; for (let i = 1; i <= 50; i++) { const option = document.createElement("option"); option.value = i; option.textContent = i; rangeComboBox.appendChild(option); } rangeComboBox.addEventListener("change", function () { const startCellId = startCellIdInput.value; const startParts = startCellId.split('-'); const startNumber = parseInt(startParts[2], 10); const comboValue = parseInt(rangeComboBox.value, 10); const totalDays = startNumber + comboValue; const endCellId = `${startParts[0]}-${startParts[1]}-${totalDays}`; console.log("Valeur sélectionnée :", comboValue); console.log("Nb jours :", totalDays); rangeResult.textContent = `Nb jours : ${totalDays}`; endCellIdInput.value = endCellId; }); startCellIdInput.value = cell.dataset.cellId; endCellIdInput.value = ""; rangeResult.textContent = "Nb jours : 0"; modal.show(); document.getElementById('confirmRangeSelection').addEventListener('click', function () { const startCellId = startCellIdInput.value; const endCellId = endCellIdInput.value; const comboValue = rangeComboBox.value; alert(startCellId + " " + endCellId); updateCellRange(startCellId, endCellId, comboValue); modal.hide(); }); document.getElementById('cancelRangeSelection').addEventListener('click', function () { modal.hide(); }); const modalHeader = modal._dialog.querySelector('.modal-header'); let isDragging = false; let offsetX, offsetY; modalHeader.addEventListener('mousedown', function (event) { isDragging = true; offsetX = event.clientX - modal._dialog.offsetLeft; offsetY = event.clientY - modal._dialog.offsetTop; modal._dialog.style.pointerEvents = 'none'; }); document.addEventListener('mousemove', function (event) { if (isDragging) { modal._dialog.style.left = `${event.clientX - offsetX}px`; modal._dialog.style.top = `${event.clientY - offsetY}px`; } }); document.addEventListener('mouseup', function () { isDragging = false; modal._dialog.style.pointerEvents = 'all'; }); endCellIdInput.addEventListener('input', function () { const startParts = startCellIdInput.value.split('-'); const endParts = endCellIdInput.value.split('-'); if (startParts.length === 3 && endParts.length === 3 && startParts[0] === endParts[0] && startParts[1] === endParts[1]) { const startValue = parseInt(startParts[2], 10); const endValue = parseInt(endParts[2], 10); console.log(" ++ " + startValue + " " + endValue); const result = endValue - startValue; rangeResult.textContent = `Nb jours : ${result} jours`; if (result > 0) { alert(`Nb jours : ${result} jours`); } } else { rangeResult.textContent = "Nb jours : 0"; } }); } function updateCellRange(startCellId, endCellId) { const startCell = document.querySelector(`td[data-cell-id="${startCellId}"]`); const endCell = document.querySelector(`td[data-cell-id="${endCellId}"]`); if (startCell && endCell && startCell.parentElement === endCell.parentElement) { const startIndex = Array.from(startCell.parentElement.cells).indexOf(startCell); const endIndex = Array.from(endCell.parentElement.cells).indexOf(endCell); for (let i = startIndex; i <= endIndex; i++) { const cell = startCell.parentElement.cells[i]; cell.dataset.cellValue = "1"; cell.textContent = "1"; cell.style.backgroundColor = "green"; cell.dataset.cellColor = "green"; updateRowData(cell); } updateCountCell(startCell.parentElement); } else { showMessageModal("Les cellules doivent être sur la même ligne."); } } tableBody.addEventListener("mouseover", function (event) { const cell = event.target.closest("td"); if (cell) { currentCell = cell; } }); document.addEventListener("keydown", function (event) { if (event.key === "p" && currentCell) { openRangeSelectionModal(currentCell); } }); /// 555 document.getElementById("buildButton").addEventListener("click", function () { console.log("Build ..."); logRowTypes(); //const tableData = traceTable(); //console.log("Building data... " + tableData); }); /* function aaaaalogRowTypes() { const rows = tableBody.querySelectorAll("tr"); const rowTypes = { standard: 0, estimation: 0, special: 0 }; rows.forEach(row => { const rowId = row.dataset.rowId; if (rowId.startsWith("standard-")) { rowTypes.standard++; } else if (rowId.startsWith("estimation-")) { rowTypes.estimation++; } else if (rowId.startsWith("special-")) { rowTypes.special++; } }); console.log("Nombre de lignes standard :", rowTypes.standard); console.log("Nombre de lignes d'estimation :", rowTypes.estimation); console.log("Nombre de lignes spéciales :", rowTypes.special); const rowDataArray = []; rows.forEach(row => { const rowId = row.dataset.rowId; console.log("Identifiant de la ligne :", rowId); rowDataArray.push(rowId); }); console.log(" ++++ " + rowDataArray); return rowDataArray; } */ function first_logRowTypes() { const rows = tableBody.querySelectorAll("tr"); const rowTypes = { standard: 0, estimation: 0, special: 0 }; rows.forEach(row => { const rowId = row.dataset.rowId; if (rowId.startsWith("standard-")) { rowTypes.standard++; } else if (rowId.startsWith("estimation-")) { rowTypes.estimation++; } else if (rowId.startsWith("special-")) { rowTypes.special++; } }); console.log("Nombre de lignes standard :", rowTypes.standard); console.log("Nombre de lignes d'estimation :", rowTypes.estimation); console.log("Nombre de lignes spéciales :", rowTypes.special); //const rowDataArray = []; rows.forEach(row => { const rowId = row.dataset.rowId; console.log("Identifiant de la ligne :", rowId); let rowData = null; if (rowId.startsWith("standard-")) { rowData = standardRowsData.find(data => data.id === rowId); } else if (rowId.startsWith("estimation-")) { rowData = estimationRowsData.find(data => data.id === rowId); } else if (rowId.startsWith("special-")) { rowData = specialRowsData.find(data => data.id === rowId); } if (rowData) { console.log("Données de la ligne :", rowData); } else { console.log("Aucune donnée trouvée pour cette ligne."); } //rowDataArray.push(rowId); }); //console.log(" ++++ " + rowDataArray); //return rowDataArray; // Envoyer les données au script PHP sendDataToPHP(rowDataArray); } function logRowTypes() { /* je veux que pour chaque identifiant contenu dans rowdate, tu passe en revue les doonées dans standardRowsData, estimationRowsData ou specialRowsData afin de trouver les doonées correpondants qui onst contunes dans l'objet dont on a li'dentifiant. */ console.log("TRACE Start"); const rowDataArray = []; const rows = tableBody.querySelectorAll("tr"); const rowTypes = { standard: 0, estimation: 0, special: 0 }; rows.forEach(row => { const rowId = row.dataset.rowId; if (rowId.startsWith("standard-")) { rowTypes.standard++; } else if (rowId.startsWith("estimation-")) { rowTypes.estimation++; } else if (rowId.startsWith("special-")) { rowTypes.special++; } let rowData = null; if (rowId.startsWith("standard-")) { rowData = standardRowsData.find(data => data.id === rowId); } else if (rowId.startsWith("estimation-")) { rowData = estimationRowsData.find(data => data.id === rowId); } else if (rowId.startsWith("special-")) { rowData = specialRowsData.find(data => data.id === rowId); } if (rowData) { console.log("Identifiant de la ligne :", rowId); console.log("Détails de l'objet :", rowData); rowDataArray.push(rowData); } else { console.log("Aucune donnée trouvée pour l'identifiant de la ligne :", rowId); } }); console.log("Nombre de lignes standard :", rowTypes.standard); console.log("Nombre de lignes d'estimation :", rowTypes.estimation); console.log("Nombre de lignes spéciales :", rowTypes.special); sendDataToPHP(rowDataArray); console.log("TRACE end"); } /* function sendDataToPHP(rowDataArray) { // Afficher les données dans la console console.log("Données envoyées à PHP :", rowDataArray); fetch('script_export.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(rowDataArray) }) .then(response => response.json()) .then(data => { console.log('Réponse du serveur :', data); }) .catch(error => { console.error('Erreur :', error); }); } */ function sendDataToPHP(rowDataArray) { // Afficher les données dans la console console.log("Données envoyées à PHP :", rowDataArray); // Envoyer les données au script PHP fetch('script_export.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(rowDataArray) }) .then(response => response.json()) .then(data => { // Gérer les différents types de messages renvoyés if (data.status === 'success') { console.log('Succès :', data.message); showMessageModal(data.message); } else if (data.status === 'error') { console.error('Erreur :', data.message); showMessageModal(data.message); } else { console.error('Réponse inattendue :', data); showMessageModal('Réponse inattendue du serveur.'); } }) .catch(error => { console.error('Erreur :', error); showMessageModal('Une erreur est survenue lors de l\'envoi des données.'); }); } // Exemple d'utilisation de la fonction fetchJsonData document.getElementById("blablaButton").addEventListener("click", fetchJsonData); function fetchJsonData() { // Envoyer une requête GET au script PHP fetch('script_import.php') .then(response => response.json()) .then(data => { // Gérer les différents types de messages renvoyés if (data.status === 'success') { console.log('Données JSON récupérées :', data.data); // Parcourir chaque élément (sous-tableau JSON) de data data.data.forEach((item, index) => { console.log("<------------------------------------>"); console.log(`Élément ${index + 1} :`); //console.log(item); //console.log(item.id); // vérifier si l'id contient standard, estimatiin ou special if (item.id.includes('standard')) { console.log('Détails de la ligne standard :'); for (const key in item) { if (item.hasOwnProperty(key)) { if (key === 'cells' && Array.isArray(item[key])) { console.log(` ${key}:`); item[key].forEach((cell, cellIndex) => { console.log(` Cell ${cellIndex + 1}:`); for (const cellKey in cell) { if (cell.hasOwnProperty(cellKey)) { console.log(` ${cellKey}: ${cell[cellKey]}`); } } }); } else { console.log(` ${key}: ${item[key]}`); } } } } else if (item.id.includes('estimation')) { console.log('Détails de la ligne d\'estimation :'); for (const key in item) { if (item.hasOwnProperty(key)) { console.log(` ${key}: ${item[key]}`); } } } else if (item.id.includes('special')) { console.log('Détails de la ligne spéciale :'); for (const key in item) { if (item.hasOwnProperty(key)) { console.log(` ${key}: ${item[key]}`); } } } else { console.log('Type de ligne inconnu :', item.id); } // Afficher les détails de chaque sous-tableau JSON /*if (item.standardRow) { console.log('Détails de la ligne standard :', item.standardRow); } if (item.estimationRow) { console.log('Détails de la ligne d\'estimation :', item.estimationRow); } if (item.specialRow) { console.log('Détails de la ligne spéciale :', item.specialRow); }*/ }); // Parcourir chaque élément (sous-tableau JSON) de data /*data.data.forEach((item, index) => { console.log(`QQQ Élément ${index + 1} :`); // Afficher les détails de chaque sous-tableau JSON if (item.standardRow) { console.log('Détails de la ligne standard :'); for (const key in item.standardRow) { if (item.standardRow.hasOwnProperty(key)) { if (key === 'cells' && Array.isArray(item.standardRow[key])) { console.log(` ${key}:`); item.standardRow[key].forEach((cell, cellIndex) => { console.log(` Cell ${cellIndex + 1}:`); for (const cellKey in cell) { if (cell.hasOwnProperty(cellKey)) { console.log(` ${cellKey}: ${cell[cellKey]}`); } } }); } else { console.log(` ${key}: ${item.standardRow[key]}`); } } } } if (item.estimationRow) { console.log('Détails de la ligne d\'estimation :'); for (const key in item.estimationRow) { if (item.estimationRow.hasOwnProperty(key)) { console.log(` ${key}: ${item.estimationRow[key]}`); } } } if (item.specialRow) { console.log('Détails de la ligne spéciale :'); for (const key in item.specialRow) { if (item.specialRow.hasOwnProperty(key)) { console.log(` ${key}: ${item.specialRow[key]}`); } } } });*/ // Vous pouvez également afficher les données dans une modale ou les utiliser dans votre application //showMessageModal('Données JSON récupérées avec succès.'); // Parcourir chaque élément (sous-tableau JSON) de data /*data.data.forEach((item, index) => { console.log(`ZZZ Élément ${index + 1} :`); // Afficher les détails de chaque sous-tableau JSON if (item.standardRow) { console.log('Détails de la ligne standard :'); for (const key in item.standardRow) { if (item.standardRow.hasOwnProperty(key)) { console.log(` ${key}:`, item.standardRow[key]); } } } if (item.estimationRow) { console.log('Détails de la ligne d\'estimation :'); for (const key in item.estimationRow) { if (item.estimationRow.hasOwnProperty(key)) { console.log(` ${key}:`, item.estimationRow[key]); } } } if (item.specialRow) { console.log('Détails de la ligne spéciale :'); for (const key in item.specialRow) { if (item.specialRow.hasOwnProperty(key)) { console.log(` ${key}:`, item.specialRow[key]); } } } });*/ // Appeler la fonction preparation() après avoir traité les données preparation(); } else if (data.status === 'error') { console.error('Erreur :', data.message); showMessageModal(data.message); } else { console.error('Réponse inattendue :', data); showMessageModal('Réponse inattendue du serveur.'); } }) .catch(error => { console.error('Erreur :', error); showMessageModal('Une erreur est survenue lors de la récupération des données.'); }); } function showMessageModal(message) { const modal = new bootstrap.Modal(document.getElementById('messageModal')); const modalBody = document.getElementById('messageModalBody'); modalBody.textContent = message; modal.show(); } // Exemple de la fonction preparation() function preparation() { console.log('La fonction preparation() a été appelée.'); purgeTable(); // Ajoutez ici le code de la fonction preparation() } function purgeTable() // objetTache { console.log(" ****************************** "); // Afficher le contenu détaillé des objets //console.log("Contenu de standardRowsData :"); standardRowsData.forEach((row, index) => { console.log(`Ligne standard ${index + 1} :`, row); }); //console.log("Contenu de estimationRowsData :"); estimationRowsData.forEach((row, index) => { console.log(`Ligne d'estimation ${index + 1} :`, row); }); //console.log("Contenu de specialRowsData :"); specialRowsData.forEach((row, index) => { console.log(`Ligne spéciale ${index + 1} :`, row); }); //console.log("Contenu de linkages :"); linkages.forEach((linkage, index) => { console.log(`Liaison ${index + 1} :`, linkage); }); console.log(" ****************************** "); standardRowsData.length = 0; estimationRowsData.length = 0; specialRowsData.length = 0; linkages.length = 0; console.log(" -------- "); if(standardRowsData.length === 0) { console.log(" standardRowsData empty"); } if(estimationRowsData.length === 0) { console.log(" estimationRowsData empty"); } if(specialRowsData.length === 0) { console.log(" specialRowsData empty"); } if(linkages.length === 0) { console.log(" linkages empty"); } //standardRowsData, estimationRowsData, specialRowsData et linkages /* standardRowsData.length = 0; estimationRowsData.length = 0; specialRowsData.length = 0; linkages.length = 0; objetTache.forEach(linkage => { standardRowsData.push(linkage.standardRow); estimationRowsData.push(linkage.estimationRow); if (linkage.specialRow) { specialRowsData.push(linkage.specialRow); } linkages.push({ linkageId: linkage.linkageId, standardRowId: linkage.standardRow.id, estimationRowId: linkage.estimationRow.id, specialRowId: linkage.specialRow ? linkage.specialRow.id : null }); }); //console.log("TRACE ++++++++++++++++++++++"); //regenerateTableAfterImport(); console.log("Objet Tache :", JSON.stringify(objetTache, null, 2));*/ } /***/ function lululululululululululogRowTypes() { const rows = tableBody.querySelectorAll("tr"); const rowTypes = { standard: 0, estimation: 0, special: 0 }; rows.forEach(row => { const rowId = row.dataset.rowId; if (rowId.startsWith("standard-")) { rowTypes.standard++; } else if (rowId.startsWith("estimation-")) { rowTypes.estimation++; } else if (rowId.startsWith("special-")) { rowTypes.special++; } // Rechercher les données correspondantes dans les différents tableaux de données let rowData = null; if (rowId.startsWith("standard-")) { rowData = standardRowsData.find(data => data.id === rowId); } else if (rowId.startsWith("estimation-")) { rowData = estimationRowsData.find(data => data.id === rowId); } else if (rowId.startsWith("special-")) { rowData = specialRowsData.find(data => data.id === rowId); } // Afficher les détails des données trouvées if (rowData) { console.log("Identifiant de la ligne :", rowId); console.log("Détails de l'objet :", rowData); } else { console.log("Aucune donnée trouvée pour l'identifiant de la ligne :", rowId); } }); console.log("Nombre de lignes standard :", rowTypes.standard); console.log("Nombre de lignes d'estimation :", rowTypes.estimation); console.log("Nombre de lignes spéciales :", rowTypes.special); } document.getElementById("logButton").addEventListener("click", function () { const tableData = traceTable(); displayJSONData(tableData); }); function displayJSONData(data) { const jsonOutputElement = document.getElementById("jsonOutput"); const jsonOutput = JSON.stringify(data, null, 2); jsonOutputElement.textContent = jsonOutput; } function createObjetTache() { const objetTache = linkages.map(linkage => { const standardRowData = standardRowsData.find(row => row.id === linkage.standardRowId); const estimationRowData = estimationRowsData.find(row => row.id === linkage.estimationRowId); const specialRowData = specialRowsData.find(row => row.id === linkage.specialRowId); return { linkageId: linkage.linkageId, standardRow: { id: standardRowData.id, name: standardRowData.name, selectValue: standardRowData.selectValue, count: standardRowData.count, cells: standardRowData.cells }, estimationRow: { id: estimationRowData.id, name: estimationRowData.name, selectValue: estimationRowData.selectValue, estimationValue: estimationRowData.estimationValue }, specialRow: specialRowData ? { id: specialRowData.id, name: specialRowData.name } : null }; }); console.log("Objet Tache :", JSON.stringify(objetTache, null, 2)); return objetTache; } document.getElementById("exportButton").addEventListener("click", function () { const objetTache = createObjetTache(); console.log("Objet Tache :", JSON.stringify(objetTache, null, 2)); fetch("export02.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(objetTache) }) .then(response => response.json()) .then(data => { if (data.status === "success") { showMessageModal("Données exportées avec succès dans le fichier json."); } else { showMessageModal("Une erreur est survenue lors de l'exportation des données."); } }) .catch(error => { console.error("Erreur :", error); showMessageModal("Une erreur est survenue lors de l'exportation des données."); }); }); document.getElementById("importButton").addEventListener("click", function () { fetch("import02.php") .then(response => response.json()) .then(data => { if (data.status === "success") { purgeAndRegenerateTable(data.objetTache); showMessageModal("Données importées avec succès."); } else { showMessageModal("Une erreur est survenue lors de l'importation des données."); } }) .catch(error => { console.error("Erreur :", error); showMessageModal("Une erreur est survenue lors de l'importation des données."); }); }); function purgeAndRegenerateTable(objetTache) { standardRowsData.length = 0; estimationRowsData.length = 0; specialRowsData.length = 0; linkages.length = 0; objetTache.forEach(linkage => { standardRowsData.push(linkage.standardRow); estimationRowsData.push(linkage.estimationRow); if (linkage.specialRow) { specialRowsData.push(linkage.specialRow); } linkages.push({ linkageId: linkage.linkageId, standardRowId: linkage.standardRow.id, estimationRowId: linkage.estimationRow.id, specialRowId: linkage.specialRow ? linkage.specialRow.id : null }); }); console.log("TRACE ++++++++++++++++++++++"); regenerateTableAfterImport(); console.log("Objet Tache :", JSON.stringify(objetTache, null, 2)); } function regenerateTableAfterImport() { tableBody.innerHTML = ""; linkages.forEach(linkage => { const standardRowData = standardRowsData.find(row => row.id === linkage.standardRowId); const estimationRowData = estimationRowsData.find(row => row.id === linkage.estimationRowId); const specialRowData = specialRowsData.find(row => row.id === linkage.specialRowId); const standardRow = document.createElement("tr"); standardRow.setAttribute("draggable", "true"); standardRow.dataset.rowId = standardRowData.id; const firstCells = [standardRowData.name, "", standardRowData.count]; firstCells.forEach((text, index) => { const td = document.createElement("td"); td.dataset.cellId = `cell-${standardRowData.id}-${index}`; if (index === 0) { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = standardRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { if (!keydownHandled && !alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; updateRowData(input); } keydownHandled = false; alertShown = false; }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { keydownHandled = true; if (!alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; updateRowData(input); } } }); td.appendChild(input); } else if (index === 1) { const select = document.createElement("select"); for (let i = 1; i <= 10; i++) { const option = document.createElement("option"); option.value = i; option.textContent = `Option ${i}`; select.appendChild(option); } select.value = standardRowData.selectValue; select.addEventListener("change", function () { updateRowData(select); }); td.appendChild(select); } else { td.textContent = text; } if (index === 0) { td.classList.add("row-header"); td.addEventListener("contextmenu", function (event) { event.preventDefault(); showContextMenu(event, standardRow); }); } standardRow.appendChild(td); }); const headerCells = document.querySelectorAll("thead tr:nth-child(2) th:not(.weekend):not(.holiday)"); const workingDaysCount = headerCells.length; for (let i = 0; i < workingDaysCount - 3; i++) { const td = document.createElement("td"); td.dataset.cellId = `cell-${standardRowData.id}-${i}`; td.dataset.cellValue = standardRowData.cells[i].value; td.dataset.cellColor = standardRowData.cells[i].color; td.textContent = standardRowData.cells[i].value; td.style.backgroundColor = standardRowData.cells[i].color; td.addEventListener("click", function () { const newValue = td.dataset.cellValue === "0" ? "1" : "0"; td.dataset.cellValue = newValue; td.textContent = newValue; td.style.backgroundColor = newValue === "1" ? "green" : "white"; td.dataset.cellColor = newValue === "1" ? "green" : "white"; updateCountCell(standardRow); updateRowData(td); }); td.addEventListener("contextmenu", function (event) { event.preventDefault(); if (td.dataset.cellValue === "1") { showColorContextMenu(event, td); } }); td.title = `ID: ${td.dataset.cellId}`; standardRow.appendChild(td); } tableBody.appendChild(standardRow); standardRow.addEventListener("dragstart", dragStart); standardRow.addEventListener("dragover", dragOver); standardRow.addEventListener("drop", drop); const estimationRow = document.createElement("tr"); estimationRow.setAttribute("draggable", "true"); estimationRow.dataset.rowId = estimationRowData.id; estimationRow.classList.add("estimation-row"); const estimationFirstCells = [estimationRowData.name, "", estimationRowData.estimationValue]; estimationFirstCells.forEach((text, index) => { const td = document.createElement("td"); td.dataset.cellId = `cell-${estimationRowData.id}-${index}`; if (index === 0) { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = estimationRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { if (!keydownHandled && !alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; updateRowData(input); } keydownHandled = false; alertShown = false; }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { keydownHandled = true; if (!alertShown) { alertShown = true; input.classList.remove("editing"); input.blur(); input.style.width = "65px"; updateRowData(input); } } }); td.appendChild(input); } else if (index === 1) { const select = document.createElement("select"); for (let i = 1; i <= 10; i++) { const option = document.createElement("option"); option.value = i; option.textContent = `Option ${i}`; select.appendChild(option); } select.value = estimationRowData.selectValue; select.addEventListener("change", function () { updateRowData(select); }); td.appendChild(select); } else { const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = text; input.addEventListener("blur", function () { const value = parseInt(input.value, 10); if (isNaN(value) || value < 1 || value > 365) { input.value = "1"; showMessageModal("La valeur doit être un nombre entier compris entre 1 et 365."); } else { updateRowData(input); } }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { const value = parseInt(input.value, 10); if (isNaN(value) || value < 1 || value > 365) { input.value = "1"; showMessageModal("La valeur doit être un nombre entier compris entre 1 et 365."); } else { updateRowData(input); } } }); td.appendChild(input); } if (index === 0) { td.classList.add("row-header"); td.addEventListener("contextmenu", function (event) { event.preventDefault(); showContextMenu(event, estimationRow); }); } estimationRow.appendChild(td); }); const mergedCell = document.createElement("td"); mergedCell.colSpan = workingDaysCount - 3; mergedCell.textContent = ""; mergedCell.classList.add("no-click"); estimationRow.appendChild(mergedCell); tableBody.appendChild(estimationRow); estimationRow.addEventListener("dragstart", dragStart); estimationRow.addEventListener("dragover", dragOver); estimationRow.addEventListener("drop", drop); if (specialRowData) { const specialRow = document.createElement("tr"); specialRow.classList.add("special-row"); specialRow.setAttribute("draggable", "true"); specialRow.dataset.rowId = specialRowData.id; const firstCell = document.createElement("td"); firstCell.classList.add("row-header"); const input = document.createElement("input"); input.type = "text"; input.classList.add("editable-input"); input.value = specialRowData.name; input.addEventListener("click", function (event) { event.stopPropagation(); input.focus(); input.style.width = "130px"; }); input.addEventListener("blur", function () { input.classList.remove("editing"); input.style.width = "65px"; updateRowData(input); }); input.addEventListener("keydown", function (event) { if (event.key === "Escape" || event.key === "Enter") { input.classList.remove("editing"); input.blur(); input.style.width = "65px"; updateRowData(input); } }); input.addEventListener("input", function () { updateRowData(input); }); const addButton = document.createElement("button"); addButton.classList.add("add-button"); addButton.textContent = "+"; addButton.addEventListener("click", function () { toggleCollapse(specialRow); }); firstCell.appendChild(input); firstCell.appendChild(addButton); firstCell.addEventListener("contextmenu", function (event) { event.preventDefault(); showSpecialContextMenu(event, specialRow); }); specialRow.appendChild(firstCell); const mergedCell = document.createElement("td"); mergedCell.colSpan = 2; mergedCell.textContent = ""; specialRow.appendChild(mergedCell); const td = document.createElement("td"); td.textContent = `${specialRowData.name} - Détails`; td.colSpan = document.querySelector("thead tr:nth-child(2)").cells.length - 3; specialRow.appendChild(td); tableBody.appendChild(specialRow); specialRow.addEventListener("dragstart", dragStart); specialRow.addEventListener("dragover", dragOver); specialRow.addEventListener("drop", drop); } }); // Afficher les liaisons dans la console displayLinkages(); } }); </script> </body> </html>