From 81c254289d9f7f5171e41e0e81b7a6d3ac19f6ee Mon Sep 17 00:00:00 2001 From: Kerboul Date: Sun, 27 Apr 2025 11:48:05 +0200 Subject: [PATCH 1/3] chore: update code structure for improved readability and maintainability --- css/base/style.css | 28 +++++++++- css/image/{camera-image => camera-image.png} | Bin js/core/routes.js | 28 +++++++++- js/pages/index.js | 54 ++++++++++++++++--- js/pages/projet_detail.js | 39 ++++++++++++++ pages/projet_detail.html | 22 ++++++++ 6 files changed, 161 insertions(+), 10 deletions(-) rename css/image/{camera-image => camera-image.png} (100%) diff --git a/css/base/style.css b/css/base/style.css index 5d1b7d8..53551ba 100644 --- a/css/base/style.css +++ b/css/base/style.css @@ -5,7 +5,7 @@ body::before { left: 0; width: 100%; height: 100%; - background-image: url("../image/camera-image"); + background-image: url("../image/camera-image.png"); background-position: center; background-size: cover; filter: blur(1px) brightness(0.3); @@ -443,4 +443,30 @@ footer { .global_title_h1 { color: white; font-size: 10vh; +} + +/* Styles pour les statuts des projets dans le carrousel */ +.status-draft { + color: #6B8E23; /* Vert haricot/marron pour les brouillons */ + font-weight: bold; +} + +.status-capturing { + color: #87CEEB; /* Bleu ciel pour les projets en capture/en cours */ + font-weight: bold; +} + +.status-idle { + color: #32CD32; /* Vert vif pour les projets terminés */ + font-weight: bold; +} + +.status-stopping { + color: #FF4500; /* Orange rougeâtre pour les projets arrêtés */ + font-weight: bold; +} + +.status-unknown { + color: #cccccc; /* Gris pour les statuts inconnus */ + font-style: italic; } \ No newline at end of file diff --git a/css/image/camera-image b/css/image/camera-image.png similarity index 100% rename from css/image/camera-image rename to css/image/camera-image.png diff --git a/js/core/routes.js b/js/core/routes.js index 0a50fe6..49cd4ca 100644 --- a/js/core/routes.js +++ b/js/core/routes.js @@ -221,4 +221,30 @@ async function stopCamera(id){ alert("Error stopping the camera :", error); throw error; } -} \ No newline at end of file +} + +async function manualUpload(imageFile, projectId, timestamp, temperature, humidity) { + try { + // Create FormData to send the file and metadata + const formData = new FormData(); + formData.append('image', imageFile); + formData.append('projectId', projectId); + formData.append('timestamp', timestamp); + formData.append('temperature', temperature); + formData.append('humidity', humidity); + + const response = await $.ajax({ + url: api_url.concat("/camera/upload"), + method: "POST", + data: formData, + processData: false, // Prevents jQuery from converting FormData to string + contentType: false, // Lets browser set the content type with boundary + }); + + alert("Image uploaded successfully"); + return response; + } catch (error) { + alert("Error uploading image: " + error); + throw error; + } +} diff --git a/js/pages/index.js b/js/pages/index.js index 62de315..f6298c0 100644 --- a/js/pages/index.js +++ b/js/pages/index.js @@ -9,18 +9,56 @@ document.addEventListener("DOMContentLoaded", () => { }); }); +function formatDateToFrench(dateString) { + const options = { + year: 'numeric', + month: 'long', + day: 'numeric' + }; + const date = new Date(dateString); + return date.toLocaleDateString('fr-FR', options); +} + +function formatStatusWithColor(status) { + let statusText = ""; + let colorClass = ""; + + switch (status) { + case 0: + statusText = "Brouillon"; + colorClass = "status-draft"; + break; + case 1: + statusText = "En capture"; + colorClass = "status-capturing"; + break; + case 2: + statusText = "Terminé"; + colorClass = "status-idle"; + break; + case 3: + statusText = "En cours d'arrêt"; + colorClass = "status-stopping"; + default: + statusText = "Inconnu"; + colorClass = "status-unknown"; + } + + return `${statusText}`; +} + function formatStatus(status) { switch (status) { case 0: - return "brouillon"; + return "Brouillon"; // Projet à peine créé, vide sans config case 1: - return "en cours"; + return "En capture"; // Projet en cours de capture case 2: - return "terminé"; + return "En pause"; // Projet en pause ou inactif case 3: - return "En cours de génération"; + return "En cours d\'arrêt"; // Projet en cours d'arrêt default: - return "inconnu"; + return "Inconnu"; } } @@ -91,10 +129,10 @@ function setupCarousel(global_project_list) { projectDiv.classList.add(letter[index]); projectDiv.innerHTML = `

${project.name}

-

${formatDate(project.start_date)}

-

Status : ${formatStatus(parseInt(project.status))}

+

Date de début : ${formatDateToFrench(project.start_date)}

+

Statut : ${formatStatusWithColor(parseInt(project.status))}

- +
`; diff --git a/js/pages/projet_detail.js b/js/pages/projet_detail.js index 6d68b15..1fbd19d 100644 --- a/js/pages/projet_detail.js +++ b/js/pages/projet_detail.js @@ -30,6 +30,40 @@ document.addEventListener("DOMContentLoaded", async () => { const firstInput = document.getElementById("first"); const lastInput = document.getElementById("last"); + const formContainerUpload = document.getElementById("form-container-upload"); + const showFormButtonUpload = document.getElementById("show-form-button-upload"); + const closeFormButtonUpload = document.getElementById("close-form-button-upload"); + const manualUploadForm = document.getElementById("manual-upload-form"); + + if (formContainerUpload && showFormButtonUpload && closeFormButtonUpload && manualUploadForm) { + showFormButtonUpload.addEventListener("click", () => { + formContainerUpload.style.display = "flex"; + }); + + closeFormButtonUpload.addEventListener("click", () => { + formContainerUpload.style.display = "none"; + }); + + manualUploadForm.addEventListener("submit", async (event) => { + event.preventDefault(); + + const imageFile = document.getElementById("imageFile").files[0]; + const temperature = document.getElementById("temperature").value; + const humidity = document.getElementById("humidity").value; + const timestamp = new Date().toISOString(); + + try { + await manualUpload(imageFile, projectId, timestamp, temperature, humidity); + alert("Image uploadée avec succès !"); + formContainerUpload.style.display = "none"; + } catch (error) { + alert("Erreur lors de l'upload de l'image : " + error); + } + }); + } else { + console.error("Un ou plusieurs éléments du formulaire d'upload manuel sont introuvables."); + } + let selectedNumbers = []; populateTimelapseLogic(start_timelapse_button, projectId).then(() => { @@ -532,6 +566,11 @@ async function populateTimelapseLogic(placeholder, id) { placeholder.innerHTML = ``; + } else if (data.status == 2) { + // Ajout de la possibilité de configurer la caméra pour un projet avec statut "terminé" + placeholder.innerHTML = ``; } else { placeholder.innerHTML = ``; } diff --git a/pages/projet_detail.html b/pages/projet_detail.html index 6048967..80e22c2 100644 --- a/pages/projet_detail.html +++ b/pages/projet_detail.html @@ -108,6 +108,27 @@ + +
@@ -118,6 +139,7 @@
+
-- 2.39.5 From 178796edb44dece9dc9d88d034469887d73005ac Mon Sep 17 00:00:00 2001 From: arussac Date: Sun, 27 Apr 2025 11:10:48 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Ajout=20de=20styles=20pour=20le=20conteneur?= =?UTF-8?q?=20de=20boutons=20et=20d'un=20message=20d'explication=20dans=20?= =?UTF-8?q?la=20page=20de=20d=C3=A9tails=20du=20projet.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/pages/style_projet.css | 13 +++++++++++++ js/pages/projet_detail.js | 12 ++++++++++-- pages/projet_detail.html | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/css/pages/style_projet.css b/css/pages/style_projet.css index 8c45e48..60c5ac5 100644 --- a/css/pages/style_projet.css +++ b/css/pages/style_projet.css @@ -163,4 +163,17 @@ .highlight{ background-color: red; +} + +.middle-place{ + position: absolute; + top: 45%; + left: 40%; +} +.explaination-text{ + font-size: 1.5em; + color: #ffffff; + text-align: center; + margin-top: 20px; + margin-bottom: 20px; } \ No newline at end of file diff --git a/js/pages/projet_detail.js b/js/pages/projet_detail.js index 1fbd19d..8067648 100644 --- a/js/pages/projet_detail.js +++ b/js/pages/projet_detail.js @@ -294,14 +294,14 @@ document.addEventListener("DOMContentLoaded", async () => { firstInput.max = lastValue; lastInput.min = firstValue; } - if (DataMetrics != 404) { - const project = data.find((project) => project.id == projectId); + const project = data.find((project) => project.id == projectId); if (project) { for (obj of document.getElementsByClassName("name_project")) obj.innerHTML = project.name; } else { console.error("Project not found"); } + if (DataMetrics != 404) { choiceSelect.addEventListener("change", toggleContainers); @@ -318,6 +318,14 @@ document.addEventListener("DOMContentLoaded", async () => { for (el of document.querySelectorAll(".box")) { el.style.display = "none"; } + document.getElementById("buttons-container").classList.add("middle-place"); + document.getElementById("toggle-view").style.display = "none"; + // add an explaination to the user + const explaination = document.createElement("p"); + explaination.innerHTML = + "Ce projet est encore vierge, il n'y a pas de données à afficher.
Veuillez configurer la caméra pour commencer à prendre des images."; + explaination.classList.add("explaination-text"); + document.getElementById("title-global").appendChild(explaination); } }); diff --git a/pages/projet_detail.html b/pages/projet_detail.html index 80e22c2..8998142 100644 --- a/pages/projet_detail.html +++ b/pages/projet_detail.html @@ -132,7 +132,7 @@
-
+
-- 2.39.5 From 091133ca9963b897ad47d26b7e10d9e7a0a11da8 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Sun, 27 Apr 2025 12:34:59 +0200 Subject: [PATCH 3/3] =?UTF-8?q?Am=C3=A9lioration=20des=20messages=20d'aler?= =?UTF-8?q?te=20et=20gestion=20des=20erreurs=20pour=20la=20configuration?= =?UTF-8?q?=20et=20l'arr=C3=AAt=20de=20la=20cam=C3=A9ra.=20Ajout=20de=20v?= =?UTF-8?q?=C3=A9rifications=20pour=20emp=C3=AAcher=20la=20suppression=20d?= =?UTF-8?q?e=20projets=20en=20cours=20de=20capture.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/core/routes.js | 9 ++-- js/pages/index.js | 11 ++++- js/pages/projet_detail.js | 95 ++++++++++++++++++++++++++++----------- 3 files changed, 85 insertions(+), 30 deletions(-) diff --git a/js/core/routes.js b/js/core/routes.js index 49cd4ca..c116ad6 100644 --- a/js/core/routes.js +++ b/js/core/routes.js @@ -197,8 +197,10 @@ async function start_timelapse(id,frequency,nbimages){ data: mydata }); - alert("data retrieval started:", response); + alert("Configuration de la caméra réussie"); + return response; } catch (error) { + alert("Erreur lors de la configuration de la caméra: " + error); throw error; } } @@ -216,9 +218,10 @@ async function stopCamera(id){ data: mydata }); - alert("Camera stopped succesfully :", response); + alert("La caméra a été arrêtée avec succès"); + return response; } catch (error) { - alert("Error stopping the camera :", error); + alert("Erreur lors de l'arrêt de la caméra : " + error); throw error; } } diff --git a/js/pages/index.js b/js/pages/index.js index f6298c0..8cfc2f7 100644 --- a/js/pages/index.js +++ b/js/pages/index.js @@ -37,8 +37,9 @@ function formatStatusWithColor(status) { colorClass = "status-idle"; break; case 3: - statusText = "En cours d'arrêt"; + statusText = "En cours d\'arrêt"; colorClass = "status-stopping"; + break; // Ajout du break manquant ici default: statusText = "Inconnu"; colorClass = "status-unknown"; @@ -149,6 +150,14 @@ function setupCarousel(global_project_list) { deleteButton.addEventListener('click', (event) => { event.stopPropagation(); const projectName = project.name; + const projectStatus = parseInt(project.status); + + // Vérifier si le projet est en cours de capture (statut 1) ou en cours d'arrêt (statut 3) + if (projectStatus === 1 || projectStatus === 3) { + alert(`Impossible de supprimer "${projectName}" car sa capture est ${projectStatus === 1 ? 'en cours' : 'en cours d\'arrêt'}.\nVeuillez d'abord arrêter la procédure de capture.`); + return; + } + document.getElementById('alertMessage').textContent = `Veux-tu vraiment supprimer le projet : ${projectName} ?`; document.getElementById('customAlert').style.display = 'block'; diff --git a/js/pages/projet_detail.js b/js/pages/projet_detail.js index 8067648..e28e6d7 100644 --- a/js/pages/projet_detail.js +++ b/js/pages/projet_detail.js @@ -85,10 +85,14 @@ document.addEventListener("DOMContentLoaded", async () => { const minutes = document.getElementById("minutes").value; const frequency = days * 1440 + hours * 60 + minutes; if(frequency >= 3) { - const nbrimages = document.getElementById("totalImages").value; - start_timelapse(projectId, frequency, nbrimages).then(() => { + const nbrimages = document.getElementById("totalImages").value; + try { + // Attendre que la requête API soit terminée avant de recharger la page + await start_timelapse(projectId, frequency, nbrimages); location.reload(); - }); + } catch (error) { + console.error("Erreur lors du démarrage du timelapse:", error); + } } else { alert("La fréquence doit être supérieure à 3 minutes !"); } @@ -98,8 +102,13 @@ document.addEventListener("DOMContentLoaded", async () => { document .getElementById("stop-camera") .addEventListener("click", async () => { - stopCamera(projectId); - location.reload(); + try { + // Attendre que la requête API soit terminée avant de recharger la page + await stopCamera(projectId); + location.reload(); + } catch (error) { + console.error("Erreur lors de l'arrêt de la caméra:", error); + } }); } }); @@ -379,24 +388,44 @@ async function generateViewMetric(projectId) { if (measurements != 404) { let samples; if (videoId != -1) { - currentVideoDatas = await getDataVideoFromApi(videoId); - samples = JSON.parse(currentVideoDatas[0]["measurement_ids"]); - if (currentVideoDatas[0].status != 0) { - videoPlaceHolder.innerHTML = ` - `; - } else { - videoPlaceHolder.innerHTML = `

La vidéo n'a pas été rendered

`; + try { + console.log("videoId", videoId); + currentVideoDatas = await getDataVideoFromApi(videoId); + console.log("currentVideoDatas", currentVideoDatas); + // Vérifier que currentVideoDatas existe et contient au moins un élément + if (currentVideoDatas) { + samples = JSON.parse(currentVideoDatas["measurement_ids"]); + if (currentVideoDatas.status != 0) { + videoPlaceHolder.innerHTML = ` + `; + } else { + videoPlaceHolder.innerHTML = `

La vidéo n'a pas été rendered

`; + } + deletePlaceHolder.innerHTML = ` + + `; + document.getElementById("delete-video").addEventListener("click", () => { + showConfirmationAlert(videoId); + }); + tempoMeasure = filterAndSortMeasurementsByNumber(measurements, samples); + } else { + // Si aucune donnée n'est disponible pour cette vidéo + console.warn("Aucune donnée disponible pour la vidéo sélectionnée"); + deletePlaceHolder.innerHTML = ""; + videoPlaceHolder.innerHTML = `

Données de vidéo non disponibles

`; + samples = measurements.map((measurement) => measurement.id); + tempoMeasure = filterAndSortMeasurementsByIds(measurements, samples); + } + } catch (error) { + console.error("Erreur lors de la récupération des données vidéo:", error); + deletePlaceHolder.innerHTML = ""; + videoPlaceHolder.innerHTML = `

Erreur lors de la récupération des données vidéo

`; + samples = measurements.map((measurement) => measurement.id); + tempoMeasure = filterAndSortMeasurementsByIds(measurements, samples); } - deletePlaceHolder.innerHTML = ` - - `; - document.getElementById("delete-video").addEventListener("click", () => { - showConfirmationAlert(videoId); - }); - tempoMeasure = filterAndSortMeasurementsByNumber(measurements, samples); } else { deletePlaceHolder.innerHTML = ""; videoPlaceHolder.innerHTML = ""; @@ -530,10 +559,19 @@ function filterAndSortMeasurementsByNumber(measurements, Numbers) { function checkVideoPath(videos, name) { let res = true; - videos.forEach((video) => { - const videoName = video.name; - if (videoName == name) res = false; - }); + + // Vérifier si videos est un tableau et s'il a une méthode forEach + if (videos && Array.isArray(videos) && videos.length > 0) { + videos.forEach((video) => { + const videoName = video.name; + if (videoName == name) res = false; + }); + } else { + // Si videos n'est pas un tableau valide, on considère qu'aucune vidéo n'existe + // et donc n'importe quel nom est valide + console.log("Aucune vidéo existante détectée"); + } + return res; } @@ -579,6 +617,11 @@ async function populateTimelapseLogic(placeholder, id) { placeholder.innerHTML = ``; + } else if (data.status == 3) { + // Affichage d'un message pour indiquer que l'arrêt est en cours + placeholder.innerHTML = ``; } else { placeholder.innerHTML = ``; } -- 2.39.5