From 68d5fda74a68f6b930340d5c3b5e46f884606a73 Mon Sep 17 00:00:00 2001
From: Myriam Delaruelle <myriam.delaruelle@univ-lorraine.fr>
Date: Thu, 23 May 2024 10:06:54 +0200
Subject: [PATCH] caching table and added popup to display a cell details on
 click

---
 amd/build/suivi-table.js              |  65 ++++++++++--
 amd/build/suivi.js                    |  72 ++++++++-----
 classes/observer.php                  |  22 ++++
 classes/output/renderer.php           |  13 +++
 db/caches.php                         |  11 ++
 db/events.php                         |  19 ++++
 lang/en/format_iena.php               |   3 +-
 lang/fr/format_iena.php               |   3 +-
 styles.css                            |  12 ++-
 suivi_edit.php                        |   9 --
 suivi_unit.php                        | 141 ++++++++++++++++++++++----
 templates/iena_activity_info.mustache |  97 ++++++++++++++++++
 templates/modal-details.mustache      |  49 +++++++++
 templates/suivi-table.mustache        |  24 +++--
 templates/suivi.mustache              |   2 +
 version.php                           |   2 +-
 16 files changed, 471 insertions(+), 73 deletions(-)
 create mode 100644 classes/observer.php
 create mode 100644 db/caches.php
 create mode 100644 db/events.php
 create mode 100644 templates/iena_activity_info.mustache
 create mode 100644 templates/modal-details.mustache

diff --git a/amd/build/suivi-table.js b/amd/build/suivi-table.js
index 19bb336..4ea69f8 100644
--- a/amd/build/suivi-table.js
+++ b/amd/build/suivi-table.js
@@ -21,8 +21,8 @@
  * @copyright  2021 Myriam Delaruelle
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-define(['jquery'],
-       function($) {
+define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
+       function($, ajax, templates, str) {
        	var all_selected=false;
         nb_results=$("#table-body input").length;
         $("#select-actions-suivi-iena").prop("disabled", true);
@@ -30,15 +30,60 @@ define(['jquery'],
           
        		registerSelectAll:function(){
        			all_selected=false;
-            if(!$._data( $('#iena-select-all')[0], 'events' )){
-         			$('#iena-select-all').on('click', function(e){
-  					     select_all_studs(e);
-  	    		  });
-              $(".checkstudent").on("click", function(){
-                checkSelectActions();
-              })
+                if(!$._data( $('#iena-select-all')[0], 'events' )){
+             			$('#iena-select-all').on('click', function(e){
+      					     select_all_studs(e);
+      	    		  });
+                  $(".checkstudent").on("click", function(){
+                    checkSelectActions();
+                  })
+                }
+       		},
+                //Si l'option de détails au clic est activée, on register l'événement du clic
+            registerDetailsClick:function(){
+                completeUrl="http://localhost/moodle4/moodle/course/format/iena/suivi_unit.php?courseid=3&sectionid=7&groupid=0&filter=%E2%89%A50";
+                $(".pointer-help").on("click", function(e){
+                    e.stopPropagation();
+                    idmodule=$(this).data("module");
+                    idetudiant=$(this).parent().data("userid");
+                    nometudiant=$(this).siblings('.third-column-iena')[0].innerHTML;
+                    console.log(nometudiant);
+                    
+                    //$('#myModal').modal('show');
+                    //$('#myModal').modal('hide');
+                    $.ajax({ 
+                        url: completeUrl,
+                        data: {action: "details", idetudiant: idetudiant, idmodule: idmodule},
+                        type: 'post',
+                        success: function(request) { 
+                           parsedrequest=JSON.parse(request)
+                            console.log(parsedrequest);
+                            parsedrequest["student"]=nometudiant;
+                            
+                             //$("#modal-content").html(parsedrequest["completioninfos"]);
+                            //data=calcPercentage(JSON.parse(request));
+                            //On récupère les détails, on va les donner à la modale
+                            //
+                            templates.render('format_iena/modal-details', parsedrequest)
+                            .done(function(html, js){
+                                
+                                 $("#container-modal").html(html);
+                                 console.log($("#container-modal"));
+                                 $('#details-modal').modal('toggle');
+                                 //templates.runTemplateJS(js);
+                                 //window.history.pushState('suivi',"", url);
+                                 //initHeaders();
+                                 
+                            })
+                            .fail(function(){
+                                loadMessage("error");
+                            }); 
+                        }
+                    });
+
+                })
             }
-       		}
+
        	}
 
    
diff --git a/amd/build/suivi.js b/amd/build/suivi.js
index 2f3bc67..910d804 100644
--- a/amd/build/suivi.js
+++ b/amd/build/suivi.js
@@ -55,11 +55,36 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
                 });
                
 	    		
-	    		changeSection();
-                registerSelectAllActivitiesFilter();
-	    		/*calcPercentage(data);
-	    		changeCompletion();*/
-       		}
+	    		
+                //registerSelectAllActivitiesFilter();
+       		}, 
+
+       		//Evenement pour le selectall : créé une seule fois au chargement de la page
+		    registerSelectAllActivitiesFilter:function(){
+		        $('.selectall').click(function() {
+		            if ($(".selectall").is(':checked')) {
+		                checkAllActivitiesFilter();
+		                displayActivities();
+		            } else {
+		                $('.option').prop('checked', false);
+		                var message=str.get_string('selectedActivities', 'format_iena');
+		                $.when(message).done(function(localizedEditString) {
+		                    $(".dropdown-text").html('0 '+localizedEditString);
+		                }); 
+		                $(".select-text").html(' Select');
+		                displayActivities();
+		            }
+		        });
+		        //initActivityFilter();
+		    },
+		    initTable:function(){
+		    	initActivityFilter(false);
+		    	displayTotalActivitiesFilter(false);
+        		displayActivities(false);
+        		changeSection(false);
+        		reloadTable(data);
+
+		    }
        		
 
        		
@@ -111,16 +136,15 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
         	data: {action: 'test'},
         	type: 'post',
         	success: function(request) { 
+        		console.log(request);
         		data=calcPercentage(JSON.parse(request));
-        		//changeCompletion();
         		changeSection();
-         		//reloadTable(JSON.parse(request));	
 			}
 		});
     }
 
   
-    function changeCompletion(){
+    function changeCompletion(changefilter=true){
     	symbol=$("#symbol-select").val();
     	filter=$("#filter-select").val();
     	data.count_results=0;
@@ -177,12 +201,15 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
     		}
     		
     	}
-    	reloadTable(data);
+    	if(changefilter==true){
+    		reloadTable(data);
+    	}
+    	
     }
 
     //on va devoir calculer le pourcentage, check la completion et reload la table à chaque fois
 
-    function changeSection(){
+    function changeSection(changefilter=true){
     	loadMessage("loading");
     	sectionid=$("#section-select").val();
     	data.count_results=0;
@@ -203,12 +230,12 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
     		}
     	}
     	
-    	if(data.count_results==0){
+    	if(data.count_results==0 && changefilter ==true){
     		reloadTable(data);
     	}
     	else{
     		data=calcPercentage(data);
-    		changeCompletion();
+    		changeCompletion(changefilter);
     	}
         changeActivitiesDropdown(data.modules);
     }
@@ -228,7 +255,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
         checkAllActivitiesFilter();
     }
 
-    function displayActivities(){
+    function displayActivities(changefilter=true){
         var arrayActivitiesID=[];
         var activitiesToStore=[];
         var activities = $('input[name="options[]"]');
@@ -252,8 +279,10 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
           
         }
         localStorage.setItem("activities",JSON.stringify(activitiesToStore));
-        //changeCompletion();
-        reloadTable(data);
+        if(changefilter==true){
+        	reloadTable(data);
+        }
+        
     }
 
 
@@ -477,11 +506,10 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
 			data.students[i].percentage=Math.floor(100 * done / nb_modules);
 		}
 		return data;
-		//reloadTable(data);
 	}
 
     //Evenement pour le selectall : créé une seule fois au chargement de la page
-    function registerSelectAllActivitiesFilter(){
+    /*function registerSelectAllActivitiesFilter(){
         $('.selectall').click(function() {
             if ($(".selectall").is(':checked')) {
                 checkAllActivitiesFilter();
@@ -497,7 +525,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
             }
         });
         initActivityFilter();
-    }
+    }*/
 
 
     //On va recharger dynamiquement les options donc on doit recréer les événements à chaque fois qu'on change de section
@@ -536,15 +564,11 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
                 $(".dropdown-text").html(total + ' '+localizedEditString);
             }); 
         }
-        //displayActivities();
-          
-
     }
   
     //Stocke le filtre des activités en localStorage pour ne pas avoir 50 id dans l'url
     //On fait bien attention à rétablir cocher le selectall si toutes les activités sont bien cochées
     function initActivityFilter(){
-
         if(localStorage.activities){
             var checkedActivities=JSON.parse(localStorage.activities);
             var activities=$("input[type='checkbox'].justone");
@@ -568,8 +592,8 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'],
             
 
         }
-        displayTotalActivitiesFilter();
-        displayActivities();
+        //displayTotalActivitiesFilter();
+        //displayActivities();
     }
 
     //Coche toutes les activités quand on change de section
diff --git a/classes/observer.php b/classes/observer.php
new file mode 100644
index 0000000..7a98da3
--- /dev/null
+++ b/classes/observer.php
@@ -0,0 +1,22 @@
+<?php
+defined('MOODLE_INTERNAL') || die();
+
+
+class format_iena_observer {
+    /**
+     * Observer that monitors course module suppressed event and update the linked matrix competencies .
+     *
+     * @param \core\event\course_module_deleted $event the event object.
+     */
+    /*public static function course_module_deleted(\core\event\course_module_deleted $event) {
+        
+    }
+
+    public static function course_module_created(\core\event\course_module_created $event) {
+        
+    }*/
+
+    public static function user_module_completion(\core\event\course_module_completion_updated $event){
+        cache_helper::purge_by_event('iena_newprogress');
+    }
+}
diff --git a/classes/output/renderer.php b/classes/output/renderer.php
index 6755a93..7074d2d 100644
--- a/classes/output/renderer.php
+++ b/classes/output/renderer.php
@@ -111,5 +111,18 @@ class renderer extends section_renderer {
         echo $this->render_from_template('format_iena/suivi-edit', $data);
     }
 
+    /**
+     * Renders the activity information.
+     *
+     * Defer to template.
+     *
+     * @param \core_course\output\activity_information $page
+     * @return string html for the page
+     */
+    public function render_activity_information(\core_course\output\activity_information $page) {
+        $data = $page->export_for_template($this->output);
+        return $this->output->render_from_template('format_iena/iena_activity_info', $data);
+    }
+
 
 }
diff --git a/db/caches.php b/db/caches.php
new file mode 100644
index 0000000..3686d16
--- /dev/null
+++ b/db/caches.php
@@ -0,0 +1,11 @@
+<?php
+
+defined('MOODLE_INTERNAL') || die;
+
+$definitions = array(
+    'students' => array(
+        'mode' => cache_store::MODE_SESSION,
+        'invalidationevents' => ['iena_newprogress']
+
+    )
+);
diff --git a/db/events.php b/db/events.php
new file mode 100644
index 0000000..4324bfe
--- /dev/null
+++ b/db/events.php
@@ -0,0 +1,19 @@
+<?php
+defined('MOODLE_INTERNAL') || die();
+
+$observers = array(
+    /*array(
+        'eventname'   => '\core\event\course_module_deleted',
+        'callback'    => 'block_competency_iena_observer::course_module_deleted',
+    ),
+    array(
+        'eventname'   => '\core\event\course_module_created',
+        'callback'    => 'block_competency_iena_observer::course_module_created',
+    ),*/
+    array(
+        'eventname'   => '\core\event\course_module_completion_updated',
+        'callback'    => 'format_iena_observer::user_module_completion',
+    )
+);
+
+?>
\ No newline at end of file
diff --git a/lang/en/format_iena.php b/lang/en/format_iena.php
index 2afd5a2..73f9a54 100644
--- a/lang/en/format_iena.php
+++ b/lang/en/format_iena.php
@@ -108,4 +108,5 @@ $string['display_status']="Enable status \"Waiting for an evaluation from me\"";
 $string['display_groups_help']="Add a column to the table which display the student's group";
 $string['display_custom_help']="This option enables the table customization and allows you to add colors to certain activities (just for you), or to define milestones (for all teachers in the course)";
 $string['display_details_help']="this option allows you to have the details of an assessment on click: grade, date of evaluation, evaluator, etc.";
-$string['display_status_help']="This option adds a new status \"Waiting for an evaluation from me\" which allows you to identify which activities have been completed by the student and require action from the teacher";
\ No newline at end of file
+$string['display_status_help']="This option adds a new status \"Waiting for an evaluation from me\" which allows you to identify which activities have been completed by the student and require action from the teacher";
+$string['cachedef_students'] = 'Students list';
\ No newline at end of file
diff --git a/lang/fr/format_iena.php b/lang/fr/format_iena.php
index 736030a..626f618 100644
--- a/lang/fr/format_iena.php
+++ b/lang/fr/format_iena.php
@@ -108,4 +108,5 @@ $string['display_status']="Activer le statut \"En attente d'évaluation de ma pa
 $string['display_groups_help']="Cette option ajoute une colonne dans le tableau contenant le nom du groupe de l'étudiant.";
 $string['display_custom_help']="Cette option active la personnalisation du tableau et permet d'ajouter des couleurs à certaines activités (personnel), ou de définir des jalons (pour tous les enseignants du cours)";
 $string['display_details_help']="Cette option permet à l'enseignant d'avoir les détails d'une évaluation au clic : note, date de l'évaluation, évaluateur...";
-$string['display_status_help']="Cette option ajoute un statut 'En attente d'évaluation de ma part qui permet de repérer quelles activités ont été complétées par l'étudiant et nécessite une action de la part de l'enseignant";
\ No newline at end of file
+$string['display_status_help']="Cette option ajoute un statut 'En attente d'évaluation de ma part qui permet de repérer quelles activités ont été complétées par l'étudiant et nécessite une action de la part de l'enseignant";
+$string['cachedef_students'] = 'La liste des étudiants du cours';
\ No newline at end of file
diff --git a/styles.css b/styles.css
index f5b1cf0..173066a 100644
--- a/styles.css
+++ b/styles.css
@@ -1043,6 +1043,10 @@ th.first-column-iena, th.second-column-iena, th.actions-column-iena, th.third-co
     border-color: #e9ecef;
 }
 
+.iena-filters #group-select{
+	max-width: 240px;
+}
+
 #count-selected-students{
     margin-top: 10px;
 }
@@ -1060,7 +1064,6 @@ th.first-column-iena, th.second-column-iena, th.actions-column-iena, th.third-co
 
 .iena-custom-dropdown .dropdown-menu{
     border-radius: 0 0 0.5rem 0.5rem; 
-    width:100%;
     max-height: 200px;
     overflow-y: auto;
 }
@@ -1193,3 +1196,10 @@ border-bottom: 1px solid #dee2e6;
 	padding-top: calc(0.375rem + 1px);
 }
 
+
+/******************************************************** Modale dans le tableau de suivi ******************************/
+#modal-completion-infos{
+  padding: 10px 12px 15px 12px;
+  background-color: #efefef;
+  margin-bottom: 10px;
+}
diff --git a/suivi_edit.php b/suivi_edit.php
index 07869ea..e040ad1 100644
--- a/suivi_edit.php
+++ b/suivi_edit.php
@@ -62,13 +62,6 @@ function init_page($course) {
 
 }
 
-function create_options_form(){
-
-}
-
- function set_data(){
-
-}
 
 $courseid = required_param('courseid', PARAM_INT);
 // Define the url of the view.
@@ -90,8 +83,6 @@ if (!has_capability('course/iena:suivi', $context = context_course::instance($co
 
 
 
-
-
     init_page($course, $PAGE);
     
     $data = [];
diff --git a/suivi_unit.php b/suivi_unit.php
index f9f72d9..f0173dc 100644
--- a/suivi_unit.php
+++ b/suivi_unit.php
@@ -27,6 +27,8 @@ require_once('view/view_param_indicateur.php');
 require_once('entity/course_format_iena_section_ressources.php');
 require_once('entity/course_format_iena_sections.php');
 require_once('entity/course_format_iena_groups.php');
+require_once($CFG->dirroot.'/course/modlib.php');
+use core_completion\cm_completion_details;
 $PAGE->requires->css('/course/format/iena/styles.css');
 
 
@@ -115,6 +117,7 @@ function format_progress($progress, $modules, $groups, $activegroupid, $activese
 
         //if ($activegroupid == 0) {
             $progressstudent->groups = "";
+            $progressstudent->groupsid=array();
             foreach ($groups as $group) {
                 if (in_array($progressstudent->id, $group->members)) {
                     if(empty( $progressstudent->groups)){
@@ -123,6 +126,7 @@ function format_progress($progress, $modules, $groups, $activegroupid, $activese
                     else{
                         $progressstudent->groups .= ", " .$group->name . " ";
                     }
+                    array_push( $progressstudent->groupsid, $group->id);
                 }
             }
         //}
@@ -140,8 +144,12 @@ function format_progress($progress, $modules, $groups, $activegroupid, $activese
 
             $progressstudent->progress[$key] = $moduleprogress;
         }
-
+        //On a les données de tous les étudiants, c'est ici qu'on filtre selon le groupe actif - DEPRECATED
+        /*if ($activegroupid == 0 || ($activegroupid>0 && in_array($activegroupid, $progressstudent->groupsid))) {
+            $students[] = $progressstudent;
+        }*/
         $students[] = $progressstudent;
+        
     }
 
     return $students;
@@ -155,7 +163,7 @@ function get_activities($completion, $activesectionid) {
         $module->id = $activity->id;
         $module->name = $activity->name;
         $displayname = format_string($activity->name, true, array('context' => $activity->context));
-        $module->displayname = strlen($displayname) > 30 ? mb_substr($displayname, 0, 29, 'UTF-8').'…' : $displayname;
+        $module->displayname = strlen($displayname) > 50 ? mb_substr($displayname, 0, 49, 'UTF-8').'…' : $displayname;
         $module->section = $activity->section;
         $modules[] = $module;
         if ($activity->section == $activesectionid || $activesectionid == 0 ) {
@@ -206,10 +214,8 @@ function set_filters($data, $filters, $symbols, $sections, $groups, $currentuser
     $data["othergroups"]=$othergroups;
     $data['current_user_groups'] = $currentusergroups;
 
-    if ($activegroupname == "") {
-        $data['default_group'] = 'selected';
-    }
-
+   
+    $data['default_group'] = 'selected';
     $data['data'] = array();
     $data['data']["sections"] = $sections;
     //$data['data']["groups"] = array_values($groups);
@@ -242,6 +248,10 @@ function set_data($data, $modules, $progress, $groups, $activegroupid, $activese
     if($listoptions->listoptions['display_groups']["value"]){
         $data["display_groups"]=1;
     }
+    if($listoptions->listoptions['display_details']["value"]){
+        $data["display_details"]=1;
+    }
+
     // Pour le téléchargement du tableau ?
     if (!isset($data['data'])) {
         $data['data'] = array();
@@ -329,16 +339,36 @@ if (isset($_GET['groupid'])) {
     $activegroupid = 0;
 }
 
-$progress = $completion->get_progress_all(
-    '',
-    array(),
-    $activegroupid,
-    'u.lastname ASC, u.firstname ASC',
-    '',
-    '',
-    $context
-);
+/*$progressrequest = $completion->get_progress_all(
+        '',
+        array(),
+        $activegroupid,
+        'u.lastname ASC, u.firstname ASC',
+        '',
+        '',
+        $context
+    );
+
+    */
+    
+$cache = cache::make('format_iena', 'students');
+if($cache->get($activegroupid) !== false){
+    $progress=$cache->get($activegroupid);
+}
+else{
+    $progressrequest = $completion->get_progress_all(
+        '',
+        array(),
+        $activegroupid,
+        'u.lastname ASC, u.firstname ASC',
+        '',
+        '',
+        $context
+    );
+    $progress=$cache->set($activegroupid, $progressrequest);
 
+    
+}
 $activesectionid = 0;
 // Section du get acquise depuis le clic sur l'indicateur dans la page du cours,
 // section depuis laquelle on a cliqué sur le bouton pour voir le suivi.
@@ -348,20 +378,95 @@ if (isset($_GET['sectionid'])) {
 
 // On récupère les ids des membres parce que dans l'excel on veut savoir qui appartient à quel groupe.
 $groups = groups_get_all_groups($COURSE->id, 0, 0, 'g.*', true);
+$renderer = $PAGE->get_renderer('format_iena');
 
+if (isset($_POST['action']) && $_POST['action'] == "details") {
+    require_once($CFG->libdir . '/gradelib.php');
+    // Si on cherche les détails d'une activité
+    $data = [];
+    $idetu=$_POST['idetudiant'];
+    $idmodule=$_POST['idmodule'];
+    $modinfo = get_fast_modinfo($COURSE->id, $idetu);
+
+
+    $cm = $modinfo->get_cm($idmodule);
+    
 
+    
+   
+    //Utile ?
+    $completioninfo = new \completion_info($COURSE);
+    $completiondetails = new cm_completion_details($completioninfo, $cm, $idetu);
+    $activitydates = \core\activity_dates::get_dates_for_module($cm, $idetu);
+
+    $activityinfo = new \core_course\output\activity_information($cm, $completiondetails, $activitydates);
+    //$activity = $activityinfo->export_for_template($renderer);
+
+    $activityhtml=$renderer->render_activity_information($activityinfo);
+    $gradesobject=array();
+    try{
+        $gradesobject=grade_get_grades($COURSE->id, 'mod', $cm->modname, $cm->instance, $idetu);
+    }
+    catch(Exception $e){
+        error_log($e);
+    }
+  
+    $lastgrade=array();
+
+    if(!empty($gradesobject)){
+        $lastgradeobject=end($gradesobject->items[0]->grades);
+        if($lastgradeobject){
+            $lastgrade["lastgrade"]=$lastgradeobject->str_long_grade;
+            $lastgrade["datelastgrade"]=$lastgradeobject->dategraded;
+        }
+        
+        $lastgrade["passgrade"]=$gradesobject->items[0]->gradepass ? number_format($gradesobject->items[0]->gradepass, 2, ',', '') : null;
+      $data["grades"]=$lastgrade;
+    }
+
+    //en cas d'achèvement manuel on créé les petites pastilles vertes ou grises (terminé ou à faire)
+    //
+
+    $data =array();
+    $data['url']=strval($cm->url);
+    $data['completion']=strval($cm->completion);
+ 
+    $data["customcompletion"]=$cm->customdata;
+    $data["completioninfos"]=$activityhtml;
+    $data["completionstate"]=$completiondetails->get_overall_completion();
+    $data["competiondetails"] = $completiondetails;
+    $data["activityname"]=$cm->name;
+  
+    echo json_encode($data);
+} 
 // If a post is sent trought the page --> Si on change les filtres, le template est appelé en JS
-if (isset($_POST['action']) && !empty($_POST['action'])) {
+else if (isset($_POST['action']) && !empty($_POST['action'])) {
     // Si on change les filtres.
     $data = [];
-    $modules = get_activities($completion, $activesectionid);
+    $progress=$cache->get($activegroupid);
 
+    $modules = get_activities($completion, $activesectionid);
+    
     $data = set_data($data, $modules, $progress, $groups, $activegroupid, $activesectionid, $listoptions);
     
     echo json_encode($data);
 } else {
+    //Si on cherche les détails d'une activité
     init_page($course, $PAGE);
     echo $OUTPUT->header();
+    /*$progress = $completion->get_progress_all(
+        '',
+        array(),
+        0,
+        'u.lastname ASC, u.firstname ASC',
+        '',
+        '',
+        $context
+    );
+
+    
+    $result = $cache->set('key', $progress);*/
+    $progress=$cache->get($activegroupid);
     $data = [];
     //Si la complétion du cours n'est pas activée on redirige l'enseignant vers l'interface pour l'activer
     if(!$COURSE->enablecompletion){
@@ -392,7 +497,7 @@ if (isset($_POST['action']) && !empty($_POST['action'])) {
             $data["msg_success"] = $msg;
         }
 }
-    $renderer = $PAGE->get_renderer('format_iena');
+ 
     $renderer->display_completion($data);
     echo $OUTPUT->footer();
 }
diff --git a/templates/iena_activity_info.mustache b/templates/iena_activity_info.mustache
new file mode 100644
index 0000000..bb7447f
--- /dev/null
+++ b/templates/iena_activity_info.mustache
@@ -0,0 +1,97 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template core_course/activity_info
+
+    Container to display activity information such as:
+      - Activity dates
+      - Activity completion requirements (automatic completion)
+      - Manual completion button
+
+    Example context (json):
+    {
+        "activityname": "Course announcements",
+        "hascompletion": true,
+        "uservisible": true,
+        "hasdates": true,
+        "isautomatic": true,
+        "istrackeduser": true,
+        "showmanualcompletion": true,
+        "activitydates": [
+            {
+                "label": "Opens:",
+                "timestamp": 1293876000
+            }
+        ],
+        "completiondetails": [
+             {
+                "statuscomplete": 1,
+                "description": "Viewed"
+            },
+            {
+                "statusincomplete": 1,
+                "description": "Receive a grade"
+            }
+        ]
+    }
+}}
+<div data-region="activity-information" data-activityname="{{activityname}}" class="activity-information">
+
+    {{#hascompletion}}
+        {{#uservisible}}
+            <div class="completion-info" data-region="completion-info">
+                {{#isautomatic}}
+                    <div class="automatic-completion-conditions" data-region ="completionrequirements" role="list" aria-label="{{#str}}completionrequirements, core_course, {{activityname}}{{/str}}">
+
+                        {{#completiondetails}}
+                            {{> core_course/completion_automatic }}
+                        {{/completiondetails}}
+                    </div>
+                {{/isautomatic}}
+                {{^isautomatic}}
+                    {{#overallcomplete}}
+                        <div class="automatic-completion-conditions" data-region="completionrequirements" role="list" aria-label="{{#str}}completionrequirements, core_course, {{activityname}}{{/str}}">
+
+                                <span class="badge badge-pill alert-success icon-no-margin" role="listitem">
+                                    <i class="icon fa fa-check fa-fw " aria-hidden="true"></i>
+                                    <strong>Terminé :</strong> <span class="font-weight-normal">Marquer l'activité comme terminée</span>
+                                </span>
+                        </div>
+                    {{/overallcomplete}}
+                    {{#overallincomplete}}
+                        <div class="automatic-completion-conditions" data-region="completionrequirements" role="list" aria-label="{{#str}}completionrequirements, core_course, {{activityname}}{{/str}}">
+
+                                <span class="badge badge-pill badge-light" role="listitem">
+                                    <strong>À faire&nbsp;:</strong> <span class="font-weight-normal">Marquer l'activité comme terminée</span>
+                                </span>
+                        </div>
+                    {{/overallincomplete}}
+                {{/isautomatic}}
+            </div>
+        {{/uservisible}}
+    {{/hascompletion}}
+
+    {{#hasdates}}
+    <div data-region="activity-dates" class="activity-dates">
+        <div class="description-inner">
+            {{#activitydates}}
+                {{>core_course/activity_date}}
+            {{/activitydates}}
+        </div>
+    </div>
+    {{/hasdates}}
+</div>
diff --git a/templates/modal-details.mustache b/templates/modal-details.mustache
new file mode 100644
index 0000000..24e5c2e
--- /dev/null
+++ b/templates/modal-details.mustache
@@ -0,0 +1,49 @@
+<div class="modal fade" id="details-modal" role="dialog">
+	<div class="modal-dialog modal-lg">
+
+		<!-- Modal content-->
+		<div class="modal-content">
+			<div class="modal-header">
+				<h4 class="modal-title">Détails de l'activité</h4>
+				<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+			</div>
+			<div class="modal-body">
+				<div class='row'>
+					
+					<div class='col-md-12 cpt-col'>
+						
+						<div><b>Etudiant : </b>{{student}}</div>
+						<div><b>Activité : </b>{{activityname}}</div>
+						<label><b>Modalités d'achèvements :</b></label>
+						<div id="modal-completion-infos">{{{completioninfos}}}
+						</div>
+						{{#grades}}
+							{{#lastgrade}}
+							<div><b>Dernière note : </b> {{lastgrade}}</div>
+							{{/lastgrade}}
+							{{#passgrade}}
+							<div><b>Note de passage : </b> {{passgrade}}</div>
+							{{/passgrade}}
+						{{/grades}}
+						<div><i class="fa fa-external-link" aria-hidden="true"></i> Aller à l'activité : <a href="{{url}}" target="_blank">{{activityname}}</a></div>
+					<br>
+						<div class="iena-footer-cpt">
+							
+							<div class='align_center'>
+						<button class='btn btn-secondary' data-dismiss="modal">Fermer</button>
+					
+					</div>
+						</div>
+						
+					</div>
+
+				</div>
+			</div>
+
+		</div>
+	</div>
+
+
+	
+
+</div>
\ No newline at end of file
diff --git a/templates/suivi-table.mustache b/templates/suivi-table.mustache
index 49ba5a0..718e82e 100644
--- a/templates/suivi-table.mustache
+++ b/templates/suivi-table.mustache
@@ -1,4 +1,4 @@
-
+{{#count_results}}
 	
 	<div class="table-wrapper">
 		<table id="suivi">
@@ -11,7 +11,7 @@
 					<th class="actions-column-iena col-header">Actions</th>
 					<th class="third-column-iena col-header">Etudiants</th>
 					{{#display_groups}}
-					<th class="third-column-iena col-header">Groupes</th>
+					<th class="group-column-iena col-header">Groupes</th>
 					{{/display_groups}}
 					{{#modules}}
 						{{#visible}}
@@ -52,7 +52,7 @@
 	
 							{{#progress}}
 								{{#visible}}
-									<td title="{{namemodule}}" data-section="{{id}}" class="pointer-help state-{{completionstate}}"><span class="icon-progress"></span></td>
+									<td title="{{namemodule}}" data-section="{{id}}" data-module="{{idmodule}}" class="pointer-help state-{{completionstate}}"><span class="icon-progress"></span></td>
 								{{/visible}}
 							{{/progress}}
 						</tr>
@@ -81,13 +81,21 @@
 	</div>
 
 <p id="count-selected-students">0 résultat(s) sélectionnés sur {{count_results}}</p>
-
-<!--{{^count_results}}
-	<div class="alert alert-primary">Il n'y a pas de résultats correspondants aux filtres sélectionnés</div>
-{{/count_results}}-->
 {{#js}}
 require(['format_iena/suivi-table'], function(module) {
     module.registerSelectAll();
+});
+{{/js}}
+{{#display_details}}
+<div id="container-modal"></div>
+{{#js}}
+require(['format_iena/suivi-table'], function(module) {
+    module.registerDetailsClick();
 
 });
-{{/js}}
\ No newline at end of file
+{{/js}}
+{{/display_details}}
+{{/count_results}}
+{{^count_results}}
+	<div class="alert alert-primary">Il n'y a pas de résultats correspondants aux filtres sélectionnés</div>
+{{/count_results}}
diff --git a/templates/suivi.mustache b/templates/suivi.mustache
index 0ad29ce..b057678 100644
--- a/templates/suivi.mustache
+++ b/templates/suivi.mustache
@@ -138,6 +138,8 @@
 require(['format_iena/suivi', 'core/templates'], function(module) {
     module.registerFilters();
    	module.registerSubmit({{{ data }}});
+   	module.registerSelectAllActivitiesFilter();
+   	module.initTable();
 });
 {{/js}}
 </div>
diff --git a/version.php b/version.php
index 2d359f4..bde89a1 100644
--- a/version.php
+++ b/version.php
@@ -26,7 +26,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2024012901;
+$plugin->version = 2024012906;
 $plugin->requires = 2014111000;
 $plugin->component = 'format_iena';
 $plugin->release = "1.0";
-- 
GitLab