diff --git a/app/api/assiduites.py b/app/api/assiduites.py
index bbd6142f912a62f89bf3f712d10f894e40bd6947..dd2be3c9cabf0555987de08fa82b292689923892 100644
--- a/app/api/assiduites.py
+++ b/app/api/assiduites.py
@@ -27,7 +27,7 @@ from app.models import (
     Justificatif,
 )
 from flask_sqlalchemy.query import Query
-from app.models.assiduites import get_assiduites_justif
+from app.models.assiduites import get_assiduites_justif, get_justifs_from_date
 from app.scodoc.sco_exceptions import ScoValueError
 from app.scodoc.sco_permissions import Permission
 from app.scodoc.sco_utils import json_error
@@ -728,7 +728,6 @@ def assiduite_edit(assiduite_id: int):
         assiduite_unique.etudiant.id,
         msg=f"assiduite: modif {assiduite_unique}",
     )
-    db.session.add(assiduite_unique)
     db.session.commit()
     scass.simple_invalidate_cache(assiduite_unique.to_dict())
 
@@ -848,15 +847,23 @@ def _edit_singular(assiduite_unique, data):
     # Cas 3 : desc
     desc = data.get("desc", False)
     if desc is not False:
-        assiduite_unique.desc = desc
+        assiduite_unique.description = desc
 
     # Cas 4 : est_just
-    est_just = data.get("est_just")
-    if est_just is not None:
-        if not isinstance(est_just, bool):
-            errors.append("param 'est_just' : booléen non reconnu")
-        else:
-            assiduite_unique.est_just = est_just
+    if assiduite_unique.etat == scu.EtatAssiduite.PRESENT:
+        assiduite_unique.est_just = False
+    else:
+        assiduite_unique.est_just = (
+            len(
+                get_justifs_from_date(
+                    assiduite_unique.etudiant.id,
+                    assiduite_unique.date_debut,
+                    assiduite_unique.date_fin,
+                    valid=True,
+                )
+            )
+            > 0
+        )
 
     if errors:
         err: str = ", ".join(errors)
@@ -1024,6 +1031,19 @@ def _filter_manager(requested, assiduites_query: Query) -> Query:
     if user_id is not False:
         assiduites_query: Query = scass.filter_by_user_id(assiduites_query, user_id)
 
+    order = requested.args.get("order", None)
+    if order is not None:
+        assiduites_query: Query = assiduites_query.order_by(Assiduite.date_debut.desc())
+
+    courant = requested.args.get("courant", None)
+    if courant is not None:
+        annee: int = scu.annee_scolaire()
+
+        assiduites_query: Query = assiduites_query.filter(
+            Assiduite.date_debut >= scu.date_debut_anne_scolaire(annee),
+            Assiduite.date_fin <= scu.date_fin_anne_scolaire(annee),
+        )
+
     return assiduites_query
 
 
diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py
index 5f66fa6030178a484308415bfc48852d60942260..e68b397ac31938de602f5ce1a0c76af7ed101eb3 100644
--- a/app/api/justificatifs.py
+++ b/app/api/justificatifs.py
@@ -130,6 +130,8 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal
 @api_web_bp.route(
     "/justificatifs/dept/<int:dept_id>/query", defaults={"with_query": True}
 )
+@bp.route("/justificatifs/dept/<int:dept_id>", defaults={"with_query": False})
+@bp.route("/justificatifs/dept/<int:dept_id>/query", defaults={"with_query": True})
 @login_required
 @scodoc
 @as_json
@@ -151,6 +153,48 @@ def justificatifs_dept(dept_id: int = None, with_query: bool = False):
     return data_set
 
 
+@bp.route(
+    "/justificatifs/formsemestre/<int:formsemestre_id>", defaults={"with_query": False}
+)
+@api_web_bp.route(
+    "/justificatifs/formsemestre/<int:formsemestre_id>", defaults={"with_query": False}
+)
+@bp.route(
+    "/justificatifs/formsemestre/<int:formsemestre_id>/query",
+    defaults={"with_query": True},
+)
+@api_web_bp.route(
+    "/justificatifs/formsemestre/<int:formsemestre_id>/query",
+    defaults={"with_query": True},
+)
+@login_required
+@scodoc
+@as_json
+@permission_required(Permission.ScoView)
+def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
+    """Retourne tous les justificatifs du formsemestre"""
+    formsemestre: FormSemestre = None
+    formsemestre_id = int(formsemestre_id)
+    formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
+
+    if formsemestre is None:
+        return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
+
+    justificatifs_query = scass.filter_by_formsemestre(
+        Justificatif.query, Justificatif, formsemestre
+    )
+
+    if with_query:
+        justificatifs_query = _filter_manager(request, justificatifs_query)
+
+    data_set: list[dict] = []
+    for justi in justificatifs_query.all():
+        data = justi.to_dict(format_api=True)
+        data_set.append(data)
+
+    return data_set
+
+
 @bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
 @api_web_bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
 @bp.route("/justificatif/etudid/<etudid>/create", methods=["POST"])
@@ -696,4 +740,19 @@ def _filter_manager(requested, justificatifs_query):
             justificatifs_query, Justificatif, formsemestre
         )
 
+    order = requested.args.get("order", None)
+    if order is not None:
+        justificatifs_query: Query = justificatifs_query.order_by(
+            Justificatif.date_debut.desc()
+        )
+
+    courant = requested.args.get("courant", None)
+    if courant is not None:
+        annee: int = scu.annee_scolaire()
+
+        justificatifs_query: Query = justificatifs_query.filter(
+            Justificatif.date_debut >= scu.date_debut_anne_scolaire(annee),
+            Justificatif.date_fin <= scu.date_fin_anne_scolaire(annee),
+        )
+
     return justificatifs_query
diff --git a/app/models/assiduites.py b/app/models/assiduites.py
index 0f8933e734927fd1d2595e86ab313120b9f566c0..994a88e07bc1a6cc190939b03f160d37e08b5eac 100644
--- a/app/models/assiduites.py
+++ b/app/models/assiduites.py
@@ -134,7 +134,10 @@ class Assiduite(db.Model):
 
         if not est_just:
             est_just = (
-                len(_get_assiduites_justif(etud.etudid, date_debut, date_fin)) > 0
+                len(
+                    get_justifs_from_date(etud.etudid, date_debut, date_fin, valid=True)
+                )
+                > 0
             )
 
         if moduleimpl is not None:
@@ -375,16 +378,23 @@ def compute_assiduites_justified(
 
 def get_assiduites_justif(assiduite_id: int, long: bool):
     assi: Assiduite = Assiduite.query.get_or_404(assiduite_id)
-    return _get_assiduites_justif(assi.etudid, assi.date_debut, assi.date_fin, long)
+    return get_justifs_from_date(assi.etudid, assi.date_debut, assi.date_fin, long)
 
 
-def _get_assiduites_justif(
-    etudid: int, date_debut: datetime, date_fin: datetime, long: bool = False
+def get_justifs_from_date(
+    etudid: int,
+    date_debut: datetime,
+    date_fin: datetime,
+    long: bool = False,
+    valid: bool = False,
 ):
-    justifs: Justificatif = Justificatif.query.filter(
+    justifs: Query = Justificatif.query.filter(
         Justificatif.etudid == etudid,
         Justificatif.date_debut <= date_debut,
         Justificatif.date_fin >= date_fin,
     )
 
+    if valid:
+        justifs = justifs.filter(Justificatif.etat == EtatJustificatif.VALIDE)
+
     return [j.justif_id if not long else j.to_dict(True) for j in justifs]
diff --git a/app/scodoc/sco_assiduites.py b/app/scodoc/sco_assiduites.py
index 3990cd82fdc5af8a6facb8a546fe7a57d6a03fa6..1ad5332d4043e50873c3673e447578317bda5605 100644
--- a/app/scodoc/sco_assiduites.py
+++ b/app/scodoc/sco_assiduites.py
@@ -311,7 +311,7 @@ def filter_by_date(
     )
 
 
-def filter_justificatifs_by_etat(justificatifs: Justificatif, etat: str) -> Query:
+def filter_justificatifs_by_etat(justificatifs: Query, etat: str) -> Query:
     """
     Filtrage d'une collection de justificatifs en fonction de leur état
     """
diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py
index e7514ed80d26074ee89dd620151b1b16cb830526..d3cd1d6bff54f11f679f058598be0879209c84ad 100644
--- a/app/scodoc/sco_preferences.py
+++ b/app/scodoc/sco_preferences.py
@@ -610,6 +610,17 @@ class BasePreferences:
                 },
             ),
             # Assiduités
+            (
+                "assi_limit_annee",
+                {
+                    "initvalue": 1,
+                    "title": "Ne lister que les assiduités de l'année",
+                    "explanation": "Limite l'affichage des listes d'assiduités et de justificatifs à l'année en cours",
+                    "input_type": "boolcheckbox",
+                    "labels": ["non", "oui"],
+                    "category": "assi",
+                },
+            ),
             (
                 "forcer_module",
                 {
diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py
index a2c951480d35639986fc611f45cabefbbdbc55b8..058c53efb6e056c6d9dfd8c7ef67f4b1a20d3ed3 100644
--- a/app/scodoc/sco_saisie_notes.py
+++ b/app/scodoc/sco_saisie_notes.py
@@ -1107,6 +1107,7 @@ def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: in
             evaluation.date_debut.date().isoformat() if evaluation.date_debut else ""
         )
         warn_abs_lst = []
+        # XXX TODO-ASSIDUITE (issue #686)
         if evaluation.is_matin():
             nbabs = 0  # TODO-ASSIDUITE sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=True)
             nbabsjust = 0  # TODO-ASSIDUITE sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=True)
diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css
index 10031d7d5c16053d8b4807d377607fb4c8b1237f..2d07244633ce6117f92c403d1817e954858b428d 100644
--- a/app/static/css/assiduites.css
+++ b/app/static/css/assiduites.css
@@ -136,6 +136,8 @@
     flex-direction: column;
     align-items: flex-start;
     margin: 0 5%;
+
+    cursor: pointer;
 }
 
 .etud_row.def .nom::after,
@@ -268,6 +270,7 @@
     background-size: cover;
 }
 
+
 .rbtn.present::before {
     background-image: url(../icons/present.svg);
 }
@@ -285,8 +288,8 @@
 }
 
 .rbtn:checked:before {
-    outline: 3px solid #7059FF;
-    border-radius: 5px;
+    outline: 5px solid #7059FF;
+    border-radius: 50%;
 }
 
 .rbtn:focus {
diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js
index 73d872ec6548c89ed8f392e193e04a8c6e5c0a86..019cb894a2849296d0a371bcc48c107e06a0ed30 100644
--- a/app/static/js/assiduites.js
+++ b/app/static/js/assiduites.js
@@ -1065,8 +1065,22 @@ function actualizeEtudAssiduite(etudid) {
   });
 }
 
-function getAllAssiduitesFromEtud(etudid, action) {
-  const url_api = getUrl() + `/api/assiduites/${etudid}`;
+function getAllAssiduitesFromEtud(
+  etudid,
+  action,
+  order = false,
+  justifs = false,
+  courant = false
+) {
+  const url_api =
+    getUrl() +
+    `/api/assiduites/${etudid}${
+      order
+        ? "/query?order%°"
+            .replace("%", justifs ? "&with_justifs" : "")
+            .replace("°", courant ? "&courant" : "")
+        : ""
+    }`;
 
   $.ajax({
     async: true,
@@ -1240,12 +1254,10 @@ function generateEtudRow(
   
         <img class="pdp" src="${pdp_url}">
   
-        <div class="name_set">
-  
+        <a class="name_set" href="BilanEtud?etudid=${etud.id}">
             <h4 class="nom">${etud.nom}</h4>
             <h5 class="prenom">${etud.prenom}</h5>
-  
-        </div>
+        </a>
   
     </div>
     <div class="assiduites_bar">
@@ -1558,20 +1570,18 @@ function fastJustify(assiduite) {
         //créer justificatif
 
         const justif = {
-          date_debut: new moment.tz(assiduite.date_debut, TIMEZONE)
-            .add(1, "s")
-            .format(),
-          date_fin: new moment.tz(assiduite.date_fin, TIMEZONE)
-            .subtract(1, "s")
-            .format(),
+          date_debut: new moment.tz(assiduite.date_debut, TIMEZONE).format(),
+          date_fin: new moment.tz(assiduite.date_fin, TIMEZONE).format(),
           raison: raison,
           etat: etat,
         };
 
         createJustificatif(justif);
 
-        // justifyAssiduite(assiduite.assiduite_id, true);
         generateAllEtudRow();
+        try {
+          loadAll();
+        } catch {}
       };
 
       const content = document.createElement("fieldset");
@@ -1634,8 +1644,17 @@ function createJustificatif(justif, success = () => {}) {
   });
 }
 
-function getAllJustificatifsFromEtud(etudid, action) {
-  const url_api = getUrl() + `/api/justificatifs/${etudid}`;
+function getAllJustificatifsFromEtud(
+  etudid,
+  action,
+  order = false,
+  courant = false
+) {
+  const url_api =
+    getUrl() +
+    `/api/justificatifs/${etudid}${
+      order ? "/query?order°".replace("°", courant ? "&courant" : "") : ""
+    }`;
   $.ajax({
     async: true,
     type: "GET",
diff --git a/app/tables/visu_assiduites.py b/app/tables/visu_assiduites.py
index 9d1adff3002cba237e177141b5442144d025f1d3..82ba0368131f8d25e72e6a4d5d0267dab88adca4 100644
--- a/app/tables/visu_assiduites.py
+++ b/app/tables/visu_assiduites.py
@@ -114,9 +114,18 @@ class RowAssi(tb.Row):
 
         compte_justificatifs = scass.filter_by_date(
             etud.justificatifs, Justificatif, self.dates[0], self.dates[1]
-        ).count()
+        )
+
+        compte_justificatifs_att = compte_justificatifs.filter(Justificatif.etat == 2)
 
-        self.add_cell("justificatifs", "Justificatifs", f"{compte_justificatifs}")
+        self.add_cell(
+            "justificatifs_att",
+            "Justificatifs en Attente",
+            f"{compte_justificatifs_att.count()}",
+        )
+        self.add_cell(
+            "justificatifs", "Justificatifs", f"{compte_justificatifs.count()}"
+        )
 
     def _get_etud_stats(self, etud: Identite) -> dict[str, list[str, float, float]]:
         retour: dict[str, tuple[str, float, float]] = {
diff --git a/app/templates/assiduites/pages/ajout_justificatif.j2 b/app/templates/assiduites/pages/ajout_justificatif.j2
index 4cbc8f590aa41eecdb288db6d969fe51eb6df9cf..aef3d5bbdfaea240a8d8984ab41e734c25a33da5 100644
--- a/app/templates/assiduites/pages/ajout_justificatif.j2
+++ b/app/templates/assiduites/pages/ajout_justificatif.j2
@@ -19,8 +19,9 @@
                 <div class="justi-label">
                     <legend for="justi_date_debut" required>Date de début</legend>
                     <input type="datetime-local" name="justi_date_debut" id="justi_date_debut">
+                    <span>Journée entière</span> <input type="checkbox" name="justi_journee" id="justi_journee">
                 </div>
-                <div class="justi-label">
+                <div class="justi-label" id="date_fin">
                     <legend for="justi_date_fin" required>Date de fin</legend>
                     <input type="datetime-local" name="justi_date_fin" id="justi_date_fin">
                 </div>
@@ -110,16 +111,15 @@
 
     function validateFields() {
         const field = document.querySelector('.justi-form')
-        const in_date_debut = field.querySelector('#justi_date_debut');
-        const in_date_fin = field.querySelector('#justi_date_fin');
+        const { deb, fin } = getDates()
 
-        if (in_date_debut.value == "" || in_date_fin.value == "") {
+        if (deb.value == "" || fin.value == "") {
             openAlertModal("Erreur détéctée", document.createTextNode("Il faut indiquer une date de début et une date de fin."), "", color = "crimson");
             return false;
         }
 
-        const date_debut = moment.tz(in_date_debut.value, TIMEZONE);
-        const date_fin = moment.tz(in_date_fin.value, TIMEZONE);
+        const date_debut = moment.tz(deb.value, TIMEZONE);
+        const date_fin = moment.tz(fin.value, TIMEZONE);
 
         if (date_fin.isBefore(date_debut)) {
             openAlertModal("Erreur détéctée", document.createTextNode("La date de fin doit se trouver après la date de début."), "", color = "crimson");
@@ -132,14 +132,14 @@
     function fieldsToJustificatif() {
         const field = document.querySelector('.justi-form')
 
-        const date_debut = field.querySelector('#justi_date_debut').value;
-        const date_fin = field.querySelector('#justi_date_fin').value;
+        const { deb, fin } = getDates()
+
         const etat = field.querySelector('#justi_etat').value;
         const raison = field.querySelector('#justi_raison').value;
 
         return {
-            date_debut: date_debut,
-            date_fin: date_fin,
+            date_debut: deb,
+            date_fin: fin,
             etat: etat,
             raison: raison,
         }
@@ -218,11 +218,43 @@
 
     }
 
+    function dayOnly() {
+
+        if (document.getElementById('justi_journee').checked) {
+            document.getElementById("date_fin").style.display = "none";
+            document.getElementById("justi_date_debut").type = "date"
+        } else {
+            document.getElementById("date_fin").style.display = "block";
+            document.getElementById("justi_date_debut").type = "datetime-local"
+        }
+    }
+
+    function getDates() {
+        if (document.getElementById('justi_journee').checked) {
+            const date_str = document.getElementById("justi_date_debut").value
+
+            return {
+                "deb": `${date_str}T${assi_morning}`,
+                "fin": `${date_str}T${assi_evening}`,
+            }
+        }
 
+        return {
+            "deb": document.getElementById("justi_date_debut").value,
+            "fin": document.getElementById("justi_date_fin").value,
+        }
+    }
 
     const etudid = {{ sco.etud.id }};
+
+    const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false;
+    const assi_morning = '{{assi_morning}}';
+    const assi_evening = '{{assi_evening}}';
+
     window.onload = () => {
         loadAll();
+        document.getElementById('justi_journee').addEventListener('click', () => { dayOnly() });
+        dayOnly()
     }
 </script>
 {% endblock pageContent %}
\ No newline at end of file
diff --git a/app/templates/assiduites/pages/bilan_dept.j2 b/app/templates/assiduites/pages/bilan_dept.j2
index aea0bb8b067b1c9745e19c2797831bdaa66cb197..2e8ca5be96e5e91c40be5c098d6db51660a5eb0f 100644
--- a/app/templates/assiduites/pages/bilan_dept.j2
+++ b/app/templates/assiduites/pages/bilan_dept.j2
@@ -29,6 +29,7 @@
 </div>
 <script>
 
+
     function loadAll() {
         generate(defAnnee)
     }
@@ -57,7 +58,7 @@
         }
         bornes = {
             deb: `${annee}-09-01T00:00`,
-            fin: `${annee + 1}-06-30T23:59`
+            fin: `${annee + 1}-08-31T23:59`
         }
 
         defAnnee = annee;
@@ -75,10 +76,14 @@
     let defAnnee = {{ annee }};
     let bornes = {
         deb: `${defAnnee}-09-01T00:00`,
-        fin: `${defAnnee + 1}-06-30T23:59`
+        fin: `${defAnnee + 1}-08-31T23:59`
     }
     const dept_id = {{ dept_id }};
 
+    let annees = {{ annees | safe}}
+
+    annees = annees.filter((x, i) => annees.indexOf(x) === i)
+
     window.addEventListener('load', () => {
 
         filterJustificatifs = {
@@ -99,15 +104,16 @@
             }
         }
         const select = document.querySelector('#annee');
-        for (let i = defAnnee + 1; i > defAnnee - 6; i--) {
+
+        annees.forEach((a) => {
             const opt = document.createElement("option");
-            opt.value = i + "",
-                opt.textContent = i + "";
-            if (i === defAnnee) {
+            opt.value = a + "",
+                opt.textContent = `${a} - ${a + 1}`;
+            if (a === defAnnee) {
                 opt.selected = true;
             }
             select.appendChild(opt)
-        }
+        })
         setterAnnee(defAnnee)
     })
 
diff --git a/app/templates/assiduites/pages/bilan_etud.j2 b/app/templates/assiduites/pages/bilan_etud.j2
index 825844c906b2d5597e827190db9a1b4e00c02c9d..917ea38cec98e2f0864bc5ab33717a1989a79dc4 100644
--- a/app/templates/assiduites/pages/bilan_etud.j2
+++ b/app/templates/assiduites/pages/bilan_etud.j2
@@ -266,6 +266,9 @@
     const assi_date_debut = "{{date_debut}}";
     const assi_date_fin = "{{date_fin}}";
 
+    const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false;
+
+
     window.addEventListener('load', () => {
         filterAssiduites = {
             "columns": [
diff --git a/app/templates/assiduites/pages/calendrier.j2 b/app/templates/assiduites/pages/calendrier.j2
index ce8e9e0f1fea3a073ad5ea6e77f908b0ec8eb2a4..3f8099342e374bd34e155c2059eed7afa477f98e 100644
--- a/app/templates/assiduites/pages/calendrier.j2
+++ b/app/templates/assiduites/pages/calendrier.j2
@@ -354,5 +354,7 @@
         setterAnnee(defAnnee)
     };
 
+
+    function isCalendrier() { return true }
 </script>
 {% endblock pageContent %}
\ No newline at end of file
diff --git a/app/templates/assiduites/pages/liste_assiduites.j2 b/app/templates/assiduites/pages/liste_assiduites.j2
index d18021806aa6ba99079e3a0db5b2ebecfcf88264..eb1f9ade3205da7406e5e6c3a105167f9aba21e9 100644
--- a/app/templates/assiduites/pages/liste_assiduites.j2
+++ b/app/templates/assiduites/pages/liste_assiduites.j2
@@ -48,8 +48,43 @@
 
 <script>
     const etudid = {{ sco.etud.id }}
+
+    const assiduite_unique_id = {{ assi_id }};
+
+    const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false;
+
+
+    function wayForFilter() {
+        if (typeof assiduites[etudid] !== "undefined") {
+            console.log("Done")
+            let assiduite = assiduites[etudid].filter((a) => { return a.assiduite_id == assiduite_unique_id });
+
+            if (assiduite) {
+                assiduite = assiduite[0]
+                filterAssiduites["filters"] = {
+                    "obj_id": [
+                        assiduite.assiduite_id,
+                    ]
+                }
+                const obj_ids = assiduite.justificatifs ? assiduite.justificatifs.map((j) => { return j.justif_id }) : []
+                filterJustificatifs["filters"] = {
+                    "obj_id": obj_ids
+                }
+
+                loadAll();
+            }
+        } else {
+            setTimeout(wayForFilter, 250)
+        }
+    }
+
     window.onload = () => {
         loadAll();
+
+        if (assiduite_unique_id != -1) {
+            wayForFilter()
+        }
+
     }
 
 </script>
\ No newline at end of file
diff --git a/app/templates/assiduites/widgets/minitimeline.j2 b/app/templates/assiduites/widgets/minitimeline.j2
index f3febd833b923d9cae8ec5034d79d19cba72d89f..835cb30842f584e87fb824dbae68409dc50045eb 100644
--- a/app/templates/assiduites/widgets/minitimeline.j2
+++ b/app/templates/assiduites/widgets/minitimeline.j2
@@ -71,6 +71,11 @@
                         updateSelectedSelect(getCurrentAssiduiteModuleImplId());
                         updateJustifyBtn();
                     }
+                    try {
+                        if (isCalendrier()) {
+                            window.location = `ListeAssiduitesEtud?etudid=${etudid}&assiduite_id=${assiduité.assiduite_id}`
+                        }
+                    } catch { }
                 });
                 //ajouter affichage assiduites on over
                 setupAssiduiteBuble(block, assiduité);
diff --git a/app/templates/assiduites/widgets/tableau_assi.j2 b/app/templates/assiduites/widgets/tableau_assi.j2
index 78c34cf007c96264f5d83b5e7574f7069649c7ab..fd1ee7df7eea9aedcf0fdf0e97017e82a3024e3a 100644
--- a/app/templates/assiduites/widgets/tableau_assi.j2
+++ b/app/templates/assiduites/widgets/tableau_assi.j2
@@ -147,7 +147,7 @@
                         <span class="obj-content">${etat}</span>
                         </div>
                         <div id="user" class="obj-part">
-                        <span class="obj-title">Créer par</span>
+                        <span class="obj-title">Créée par</span>
                         <span class="obj-content">${user}</span>
                         </div>
                     </div>
@@ -239,7 +239,7 @@
                     edit = setModuleImplId(edit, module);
 
                     fullEditAssiduites(data.assiduite_id, edit, () => {
-                        try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { }
+                        loadAll();
                     })
 
 
diff --git a/app/templates/assiduites/widgets/tableau_base.j2 b/app/templates/assiduites/widgets/tableau_base.j2
index 5a795af1a9bdaa843c30c16df4dda21441cf83ff..2b468e6a4d9dc19264fe312bee542b38ec01ac82 100644
--- a/app/templates/assiduites/widgets/tableau_base.j2
+++ b/app/templates/assiduites/widgets/tableau_base.j2
@@ -18,6 +18,9 @@
 
     document.addEventListener("click", () => {
         contextMenu.style.display = "none";
+        if (contextMenu.childElementCount > 3) {
+            contextMenu.removeChild(contextMenu.lastElementChild)
+        }
     });
 
     editOption.addEventListener("click", () => {
@@ -94,6 +97,11 @@
                     }
                 }
 
+                if (k == "obj_id") {
+                    const obj_id = el.assiduite_id || el.justif_id;
+                    return f.obj_id.includes(obj_id)
+                }
+
                 return true;
             })
 
@@ -150,7 +158,7 @@
             paginationContainerAssiduites.querySelector('.pagination_moins').addEventListener('click', () => {
                 if (currentPageAssiduites > 1) {
                     currentPageAssiduites--;
-                    paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites
+                    paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + ""
                     assiduiteCallBack(array);
 
                 }
@@ -159,7 +167,7 @@
             paginationContainerAssiduites.querySelector('.pagination_plus').addEventListener('click', () => {
                 if (currentPageAssiduites < totalPages) {
                     currentPageAssiduites++;
-                    paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites
+                    paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + ""
                     assiduiteCallBack(array);
                 }
             })
@@ -199,8 +207,12 @@
 
             if (assi) {
                 paginationContainerAssiduites.querySelector('#paginationAssi').appendChild(paginationButton)
+                if (i == currentPageAssiduites)
+                    paginationContainerAssiduites.querySelector('#paginationAssi').value = i + "";
             } else {
                 paginationContainerJustificatifs.querySelector('#paginationJusti').appendChild(paginationButton)
+                if (i == currentPageJustificatifs)
+                    paginationContainerJustificatifs.querySelector('#paginationJusti').value = i + "";
             }
         }
         updateActivePaginationButton(assi);
@@ -230,8 +242,8 @@
     }
 
     function loadAll() {
-        try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { }
-        try { getAllJustificatifsFromEtud(etudid, justificatifCallBack) } catch (_) { }
+        try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack, true, true, assi_limit_annee) } catch (_) { }
+        try { getAllJustificatifsFromEtud(etudid, justificatifCallBack, true, assi_limit_annee) } catch (_) { }
     }
 
     function order(keyword, callback = () => { }, el, assi = true) {
@@ -641,6 +653,27 @@
         contextMenu.style.top = `${e.clientY - contextMenu.offsetHeight}px`;
         contextMenu.style.left = `${e.clientX}px`;
         contextMenu.style.display = "block";
+        if (contextMenu.childElementCount > 3) {
+            contextMenu.removeChild(contextMenu.lastElementChild)
+        }
+        if (selectedRow.getAttribute('type') == "assiduite") {
+
+            const li = document.createElement('li')
+            li.textContent = "Justifier"
+
+            li.addEventListener('click', () => {
+                let obj_id = selectedRow.getAttribute('obj_id');
+                assiduite = Object.values(assiduites).flat().filter((a) => { return a.assiduite_id == obj_id })
+                console.log(assiduite[0])
+                if (assiduite && !assiduite[0].est_just && assiduite[0].etat != "PRESENT") {
+                    fastJustify(assiduite[0])
+                } else {
+                    openAlertModal("Erreur", document.createTextNode("L'assiduité est déjà justifiée ou ne peut pas l'être."))
+                }
+            })
+
+            contextMenu.appendChild(li)
+        }
     }
 
 </script>
diff --git a/app/templates/assiduites/widgets/tableau_justi.j2 b/app/templates/assiduites/widgets/tableau_justi.j2
index c4b677733c54a228944a1751bb455dcc506b9ee1..4b3a50228986914c3e268598ddb0e99ae8805783 100644
--- a/app/templates/assiduites/widgets/tableau_justi.j2
+++ b/app/templates/assiduites/widgets/tableau_justi.j2
@@ -169,7 +169,7 @@
                         <span class="obj-content">${etat}</span>
                         </div>
                         <div id="user" class="obj-part">
-                        <span class="obj-title">Créer par</span>
+                        <span class="obj-title">Créé par</span>
                         <span class="obj-content">${user}</span>
                         </div>
                     </div>
diff --git a/app/templates/assiduites/widgets/timeline.j2 b/app/templates/assiduites/widgets/timeline.j2
index 706c5f504b38aa7223dc2c19bddddd775ce009da..576e30218b4d187e44f0d4182c8d328943b18f36 100644
--- a/app/templates/assiduites/widgets/timeline.j2
+++ b/app/templates/assiduites/widgets/timeline.j2
@@ -18,6 +18,8 @@
 
     const period_default = {{ periode_defaut }};
 
+    let handleMoving = false;
+
     function createTicks() {
         let i = t_start;
 
@@ -87,72 +89,92 @@
 
     }
 
-    function setupTimeLine(callback) {
+    function timelineMainEvent(event, callback) {
         const func_call = callback ? callback : () => { };
-        timelineContainer.addEventListener("mousedown", (event) => {
-            const startX = event.clientX;
 
-            if (event.target === periodTimeLine) {
-                const startLeft = parseFloat(periodTimeLine.style.left);
+        const startX = (event.clientX || event.changedTouches[0].clientX);
 
-                const onMouseMove = (moveEvent) => {
-                    const deltaX = moveEvent.clientX - startX;
-                    const containerWidth = timelineContainer.clientWidth;
-                    const newLeft = startLeft + (deltaX / containerWidth) * 100;
+        if (event.target.classList.contains("period-handle")) {
+            const startWidth = parseFloat(periodTimeLine.style.width);
+            const startLeft = parseFloat(periodTimeLine.style.left);
+            const isLeftHandle = event.target.classList.contains("left");
+            handleMoving = true
+            const onMouseMove = (moveEvent) => {
 
-                    adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
-
-                    updatePeriodTimeLabel();
-                };
-
-                document.addEventListener("mousemove", onMouseMove);
-                document.addEventListener(
-                    "mouseup",
-                    () => {
-                        generateAllEtudRow();
-                        snapHandlesToQuarters();
-                        document.removeEventListener("mousemove", onMouseMove);
-                        func_call();
-                    },
-                    { once: true }
-                );
-            } else if (event.target.classList.contains("period-handle")) {
-                const startWidth = parseFloat(periodTimeLine.style.width);
-                const startLeft = parseFloat(periodTimeLine.style.left);
-                const isLeftHandle = event.target.classList.contains("left");
-
-                const onMouseMove = (moveEvent) => {
-                    const deltaX = moveEvent.clientX - startX;
-                    const containerWidth = timelineContainer.clientWidth;
-                    const newWidth =
-                        startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
-
-                    if (isLeftHandle) {
-                        const newLeft = startLeft + (deltaX / containerWidth) * 100;
-                        adjustPeriodPosition(newLeft, newWidth);
-                    } else {
-                        adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
-                    }
+                if (!handleMoving) return;
 
-                    updatePeriodTimeLabel();
-                };
+                const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX;
+                const containerWidth = timelineContainer.clientWidth;
+                const newWidth =
+                    startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
 
-                document.addEventListener("mousemove", onMouseMove);
-                document.addEventListener(
-                    "mouseup",
-                    () => {
-                        snapHandlesToQuarters();
-                        generateAllEtudRow();
+                if (isLeftHandle) {
+                    const newLeft = startLeft + (deltaX / containerWidth) * 100;
+                    adjustPeriodPosition(newLeft, newWidth);
+                } else {
+                    adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
+                }
 
-                        document.removeEventListener("mousemove", onMouseMove);
+                updatePeriodTimeLabel();
+            };
+            const mouseUp = () => {
+                snapHandlesToQuarters();
+                generateAllEtudRow();
 
-                        func_call();
+                timelineContainer.removeEventListener("mousemove", onMouseMove);
+                handleMoving = false;
+                func_call();
 
-                    },
-                    { once: true }
-                );
             }
-        });
+            timelineContainer.addEventListener("mousemove", onMouseMove);
+            timelineContainer.addEventListener("touchmove", onMouseMove);
+            document.addEventListener(
+                "mouseup",
+                mouseUp,
+            );
+            document.addEventListener(
+                "touchend",
+                mouseUp,
+            );
+        } else if (event.target === periodTimeLine) {
+
+            const startLeft = parseFloat(periodTimeLine.style.left);
+
+            const onMouseMove = (moveEvent) => {
+                console.warn("move Period")
+                if (handleMoving) return;
+                const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX;
+                const containerWidth = timelineContainer.clientWidth;
+                const newLeft = startLeft + (deltaX / containerWidth) * 100;
+
+                adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
+
+                updatePeriodTimeLabel();
+            };
+            const mouseUp = () => {
+                generateAllEtudRow();
+                snapHandlesToQuarters();
+                timelineContainer.removeEventListener("mousemove", onMouseMove);
+                func_call();
+            }
+            timelineContainer.addEventListener("mousemove", onMouseMove);
+            timelineContainer.addEventListener("touchmove", onMouseMove);
+            document.addEventListener(
+                "mouseup",
+                mouseUp,
+                { once: true }
+            );
+            document.addEventListener(
+                "touchend",
+                mouseUp,
+                { once: true }
+            );
+        }
+    }
+
+    function setupTimeLine(callback) {
+        timelineContainer.addEventListener("mousedown", (e) => { timelineMainEvent(e, callback) });
+        timelineContainer.addEventListener("touchstart", (e) => { timelineMainEvent(e, callback) });
     }
 
     function adjustPeriodPosition(newLeft, newWidth) {
diff --git a/app/views/assiduites.py b/app/views/assiduites.py
index 654873b0a79f3c22d7b01878c7ba12bc8ef2dcb4..c40345c9bb0ff738c434087b47be11e4df8518cd 100644
--- a/app/views/assiduites.py
+++ b/app/views/assiduites.py
@@ -162,24 +162,35 @@ def index_html():
         """<p class="help">Pour signaler, annuler ou justifier une assiduité pour un seul étudiant, 
         choisissez d'abord le concerné:</p>"""
     )
-    H.append(sco_find_etud.form_search_etud())
-    # if current_user.has_permission(
-    #     Permission.ScoAbsChange
-    # ) and sco_preferences.get_preference("handle_billets_abs"):
-    #     H.append(
-    #         f"""
-    #         <h2 style="margin-top: 30px;">Billets d'absence</h2>
-    #         <ul><li><a href="{url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
-    #         }">Traitement des billets d'absence en attente</a>
-    #         </li></ul>
-    #         """
-    #     )
+    H.append(sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"))
+    if current_user.has_permission(
+        Permission.ScoAbsChange
+    ) and sco_preferences.get_preference("handle_billets_abs"):
+        H.append(
+            f"""
+            <h2 style="margin-top: 30px;">Billets d'absence</h2>
+            <ul><li><a href="{url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
+            }">Traitement des billets d'absence en attente</a>
+            </li></ul>
+            """
+        )
+    dept: Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first()
+    annees: list[int] = sorted(
+        [f.date_debut.year for f in dept.formsemestres],
+        reverse=True,
+    )
+
+    annees_str: str = "["
+    for ann in annees:
+        annees_str += f"{ann},"
+    annees_str += "]"
 
     H.append(
         render_template(
             "assiduites/pages/bilan_dept.j2",
             dept_id=g.scodoc_dept_id,
             annee=scu.annee_scolaire(),
+            annees=annees_str,
         ),
     )
     H.append(html_sco_header.sco_footer())
@@ -299,6 +310,8 @@ def liste_assiduites_etud():
     if etud.dept_id != g.scodoc_dept_id:
         abort(404, "étudiant inexistant dans ce département")
 
+    assiduite_id: int = request.args.get("assiduite_id", -1)
+
     header: str = html_sco_header.sco_header(
         page_title="Liste des assiduités",
         init_qtip=True,
@@ -319,6 +332,11 @@ def liste_assiduites_etud():
             "assiduites/pages/liste_assiduites.j2",
             sco=ScoData(etud),
             date=datetime.date.today().isoformat(),
+            assi_id=assiduite_id,
+            assi_limit_annee=sco_preferences.get_preference(
+                "assi_limit_annee",
+                dept_id=g.scodoc_dept_id,
+            ),
         ),
     ).build()
 
@@ -371,6 +389,10 @@ def bilan_etud():
             date_fin=date_fin,
             assi_metric=assi_metric,
             assi_seuil=_get_seuil(),
+            assi_limit_annee=sco_preferences.get_preference(
+                "assi_limit_annee",
+                dept_id=g.scodoc_dept_id,
+            ),
         ),
     ).build()
 
@@ -412,6 +434,12 @@ def ajout_justificatif_etud():
         render_template(
             "assiduites/pages/ajout_justificatif.j2",
             sco=ScoData(etud),
+            assi_limit_annee=sco_preferences.get_preference(
+                "assi_limit_annee",
+                dept_id=g.scodoc_dept_id,
+            ),
+            assi_morning=ScoDocSiteConfig.get("assi_morning_time", "08:00"),
+            assi_evening=ScoDocSiteConfig.get("assi_evening_time", "18:00"),
         ),
     ).build()
 
diff --git a/tests/unit/test_assiduites.py b/tests/unit/test_assiduites.py
index 0ed1bb506cb26c2a10939c8eec63784f4753ff03..46016f267a2ede766e2be2e168bb7420ec642e33 100644
--- a/tests/unit/test_assiduites.py
+++ b/tests/unit/test_assiduites.py
@@ -157,7 +157,6 @@ def test_general(test_client):
     editer_supprimer_justificatif(etuds[0])
 
 
-# XXX TODO-ASSIDUITE (issue #696)
 def verif_migration_abs_assiduites():
     """Vérification que le script de migration fonctionne correctement"""
     downgrade_module(assiduites=True, justificatifs=True)