diff --git a/app/scodoc/sco_archives_etud.py b/app/scodoc/sco_archives_etud.py
index b7ff7d32b07b18f30253b6bf1bf73a42fe993a0b..0067be0af139303c90f1f89b5860bed9f1bb80aa 100644
--- a/app/scodoc/sco_archives_etud.py
+++ b/app/scodoc/sco_archives_etud.py
@@ -358,7 +358,7 @@ def etudarchive_import_files(
         unmatched_files=unmatched_files,
         stored_etud_filename=stored_etud_filename,
         next_page=url_for(
-            "scolar.groups_view",
+            "scolar.groups_feuilles",
             scodoc_dept=g.scodoc_dept,
             formsemestre_id=formsemestre_id,
         ),
diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py
index 6b5d1a3a163c53524bdf8e397c83374eacbc5fb6..5f3d69cebf4d385077209ec5fc1095b738a15a4e 100755
--- a/app/scodoc/sco_formsemestre_status.py
+++ b/app/scodoc/sco_formsemestre_status.py
@@ -335,7 +335,7 @@ def formsemestre_status_menubar(formsemestre: FormSemestre | None) -> str:
         },
         {
             "title": "Exporter table des étudiants",
-            "endpoint": "scolar.groups_view",
+            "endpoint": "scolar.groups_lists",
             "args": {
                 "fmt": "allxls",
                 "group_ids": sco_groups.get_default_group(
@@ -354,12 +354,26 @@ def formsemestre_status_menubar(formsemestre: FormSemestre | None) -> str:
     can_change_groups = formsemestre.can_change_groups()
     menu_groupes = [
         {
-            "title": "Listes, photos, feuilles...",
-            "endpoint": "scolar.groups_view",
+            "title": "Listes des groupes",
+            "endpoint": "scolar.groups_lists",
             "args": {"formsemestre_id": formsemestre_id},
             "enabled": True,
             "helpmsg": "Accès aux listes des groupes d'étudiants",
         },
+        {
+            "title": "Trombinoscopes",
+            "endpoint": "scolar.groups_photos",
+            "args": {"formsemestre_id": formsemestre_id},
+            "enabled": True,
+            "helpmsg": "Accès aux photos des groupes d'étudiants",
+        },
+        {
+            "title": "Assiduité, feuilles d'appel, ...",
+            "endpoint": "scolar.groups_feuilles",
+            "args": {"formsemestre_id": formsemestre_id},
+            "enabled": True,
+            "helpmsg": "Accès aux feuilles d'appel des groupes d'étudiants",
+        },
         {
             "title": "Modifier groupes et partitions",
             "endpoint": "scolar.partition_editor",
@@ -826,7 +840,7 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str:
                     <div class="sem-groups-list">
                         <div>
                         <a class="stdlink" href="{
-                            url_for("scolar.groups_view",
+                            url_for("scolar.groups_lists",
                                 group_ids=group.id,
                                 scodoc_dept=g.scodoc_dept,
                             )
diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py
index 2114f20c78a36a53bf657c1840349dc20d8178e6..d4817ee286f1daaee3de7cad810da38f8d75deac 100644
--- a/app/scodoc/sco_groups_view.py
+++ b/app/scodoc/sco_groups_view.py
@@ -30,12 +30,13 @@ sous forme: de liste html (table exportable), de trombinoscope (exportable en pd
 """
 
 # Re-ecriture en 2014 (re-organisation de l'interface, modernisation du code)
+# Modif en 2024 (9.7/revamp, abandon des tabs bootstrap)
 
 import datetime
 from urllib.parse import parse_qs
 
 
-from flask import url_for, g, request
+from flask import url_for, g, render_template, request
 from flask_login import current_user
 
 from app import db
@@ -64,8 +65,8 @@ JAVASCRIPTS = html_sco_header.BOOTSTRAP_JS + [
 CSSSTYLES = html_sco_header.BOOTSTRAP_CSS
 
 
-# view:
-def groups_view(
+# view
+def groups_lists(
     group_ids=(),
     fmt="html",
     with_codes=0,
@@ -87,6 +88,7 @@ def groups_view(
 
     formsemestre_id est utilisé si aucun groupe selectionné pour construire la liste des groupes.
     """
+    # version sans tabs: juste la liste des étudiants
     # Informations sur les groupes à afficher:
     groups_infos = DisplayedGroupsInfos(
         group_ids,
@@ -112,60 +114,58 @@ def groups_view(
     #  - charger tous les etudiants au debut, quels que soient les groupes selectionnés
     #  - ajouter du JS pour modifier les liens (arguments group_ids) quand le menu change
 
-    return f"""
-    { html_sco_header.sco_header(
-            javascripts=JAVASCRIPTS,
-            cssstyles=CSSSTYLES
-        )
-    }
-    <style>
-    span.warning_unauthorized {{
-        color: pink;
-        font-style: italic;
-        margin-left: 12px;
-    }}
-    </style>
-    <div id="group-tabs">
-        <!-- Menu choix groupe -->
-        {form_groups_choice(groups_infos, submit_on_change=True)}
-        <ul class="nav nav-tabs" id="myTab" role="tablist">
-            <li class="nav-item" role="presentation">
-                <button class="nav-link active" id="tab-listes" data-bs-toggle="tab" data-bs-target="#tab-listes-pane" type="button" role="tab" aria-controls="tab-listes-pane" aria-selected="true">Listes</button>
-            </li>
-            <li class="nav-item" role="presentation">
-                <button class="nav-link" id="tab-photos" data-bs-toggle="tab" data-bs-target="#tab-photos-pane" type="button" role="tab" aria-controls="tab-photos-pane" aria-selected="true">Photos</button>
-            </li>
-            <li class="nav-item" role="presentation">
-                <button class="nav-link" id="tab-abs" data-bs-toggle="tab" data-bs-target="#tab-abs-pane" type="button" role="tab" aria-controls="tab-abs-pane" aria-selected="true">Absences et feuilles...</button>
-            </li>
-        </ul>
-    <!-- Tab panes -->
-    <div class="tab-content" id="myTabContent">
-        <div class="tab-pane active show" id="tab-listes-pane" role="tabpanel" aria-labelledby="tab-listes" tabindex="0">
-            {
-                groups_table(
-                    groups_infos=groups_infos,
-                    fmt=fmt,
-                    with_codes=with_codes,
-                    etat=etat,
-                    with_paiement=with_paiement,
-                    with_archives=with_archives,
-                    with_annotations=with_annotations,
-                    with_bourse=with_bourse,
-                )
-            }
-        </div>
-        <div class="tab-pane" id="tab-photos-pane" role="tabpanel" aria-labelledby="tab-photos" tabindex="0">
-            { tab_photos_html(groups_infos, etat=etat) }
-        </div>
-        <div class="tab-pane" id="tab-abs-pane" role="tabpanel" aria-labelledby="tab-abs" tabindex="0">
-            { tab_absences_html(groups_infos, etat=etat) }
-        </div>
-    </div>
-    </div>
-
-    { html_sco_header.sco_footer() }
+    return render_template(
+        "formsemestre/groups_lists.j2",
+        form_groups_choice=form_groups_choice(groups_infos, submit_on_change=True),
+        groups_table=groups_table(
+            groups_infos=groups_infos,
+            fmt=fmt,
+            with_codes=with_codes,
+            etat=etat,
+            with_paiement=with_paiement,
+            with_archives=with_archives,
+            with_annotations=with_annotations,
+            with_bourse=with_bourse,
+        ),
+        groups_titles=groups_infos.groups_titles,
+    )
+
+
+# view
+def groups_photos(group_ids=(), etat=None, formsemestre_id=None):
+    """Affichage des photos des étudiants (trombi) des groupes indiqués
+    group_ids: liste de group_id
+    formsemestre_id est utilisé si aucun groupe selectionné pour construire la liste des groupes.
+    """
+    groups_infos = DisplayedGroupsInfos(
+        group_ids,
+        formsemestre_id=formsemestre_id,
+        select_all_when_unspecified=True,
+    )
+    return render_template(
+        "formsemestre/groups_photos.j2",
+        form_groups_choice=form_groups_choice(groups_infos, submit_on_change=True),
+        tab_photos_html=tab_photos_html(groups_infos, etat=etat),
+        groups_titles=groups_infos.groups_titles,
+    )
+
+
+def groups_feuilles(group_ids=(), etat=None, formsemestre_id=None):
+    """Affichage des feuilles d'appel des groupes indiqués
+    group_ids: liste de group_id
+    formsemestre_id est utilisé si aucun groupe selectionné pour construire la liste des groupes.
     """
+    groups_infos = DisplayedGroupsInfos(
+        group_ids,
+        formsemestre_id=formsemestre_id,
+        select_all_when_unspecified=True,
+    )
+    return render_template(
+        "formsemestre/groups_feuilles.j2",
+        form_groups_choice=form_groups_choice(groups_infos, submit_on_change=True),
+        tab_absences_html=tab_absences_html(groups_infos, etat=etat),
+        groups_titles=groups_infos.groups_titles,
+    )
 
 
 def form_groups_choice(
diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py
index c2f604c359aa266371654f8ca1e5562ae2156424..5250af778c77138f7ce47569419576fb7328d354 100644
--- a/app/scodoc/sco_page_etud.py
+++ b/app/scodoc/sco_page_etud.py
@@ -254,7 +254,7 @@ def fiche_etud(etudid=None):
 
                 grlinks.append(
                     f"""<a class="discretelink" href="{
-                    url_for('scolar.groups_view',
+                    url_for('scolar.groups_lists',
                     scodoc_dept=g.scodoc_dept, group_ids=partition['group_id'])
                 }" title="Liste du groupe {gr_name}">{gr_name}</a>
                 """
diff --git a/app/scodoc/sco_trombino.py b/app/scodoc/sco_trombino.py
index 7431609bc3971a211d5c791a098132fdf0fa91c0..3ed60ec124d04d7faf5478a4d73306167f4771ef 100644
--- a/app/scodoc/sco_trombino.py
+++ b/app/scodoc/sco_trombino.py
@@ -208,8 +208,7 @@ def check_local_photos_availability(groups_infos, fmt=""):
                 >exporter seulement les photos existantes</a>""",
                 dest_url="trombino",
                 OK="Exporter seulement les photos existantes",
-                cancel_url="groups_view?curtab=tab-photos&"
-                + groups_infos.groups_query_args,
+                cancel_url="groups_photos?" + groups_infos.groups_query_args,
                 parameters=parameters,
             ),
         )
@@ -249,7 +248,7 @@ def trombino_copy_photos(group_ids=None, dialog_confirmed=False):
     "Copy photos from portal to ScoDoc (overwriting local copy)"
     group_ids = [] if group_ids is None else group_ids
     groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
-    back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
+    back_url = "groups_photos?" + str(groups_infos.groups_query_args)
 
     portal_url = sco_portal_apogee.get_portal_url()
     header = html_sco_header.sco_header(page_title="Chargement des photos")
@@ -504,7 +503,7 @@ def photos_import_files_form(group_ids=()):
     if not group_ids:
         raise ScoValueError("paramètre manquant !")
     groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
-    back_url = f"groups_view?{groups_infos.groups_query_args}&curtab=tab-photos"
+    back_url = f"groups_photos?{groups_infos.groups_query_args}"
 
     H = [
         html_sco_header.sco_header(page_title="Import des photos des étudiants"),
@@ -567,10 +566,9 @@ def photos_import_files_form(group_ids=()):
             unmatched_files=unmatched_files,
             stored_etud_filename=stored_etud_filename,
             next_page=url_for(
-                "scolar.groups_view",
+                "scolar.groups_photos",
                 scodoc_dept=g.scodoc_dept,
                 formsemestre_id=groups_infos.formsemestre_id,
-                curtab="tab-photos",
             ),
         )
 
diff --git a/app/static/js/groups_view.js b/app/static/js/groups_view.js
index 29517a325d7b57ba8dc6be2ada0b7fdc163e87f9..7f16a580d7f6241decbdb7550e810e267e9fa648 100644
--- a/app/static/js/groups_view.js
+++ b/app/static/js/groups_view.js
@@ -20,11 +20,6 @@ function groups_view_url() {
     "formsemestre_id",
     $("#group_selector")[0].formsemestre_id.value
   );
-  // ajout du tab actif
-  const tabActif = document.querySelector(
-    '[role="tab"][aria-selected="true"]'
-  ).id;
-  urlParams.set("tab", tabActif);
   urlParams.delete("group_ids");
   // ajout des groupes selectionnes
   var selected_groups = document.getElementById("group_ids_sel").value;
diff --git a/app/templates/formsemestre/groups_feuilles.j2 b/app/templates/formsemestre/groups_feuilles.j2
new file mode 100644
index 0000000000000000000000000000000000000000..2565ca0f53fdba0d3b7e78d5c6f89162eb1d3865
--- /dev/null
+++ b/app/templates/formsemestre/groups_feuilles.j2
@@ -0,0 +1,27 @@
+{# Trombinoscope HTML #}
+
+{% extends "sco_page.j2" %}
+
+
+{% block title %}
+  Feuilles {{groups_titles}}
+{% endblock title %}
+
+
+{% block app_content %}
+  <div class="pageContent">
+    <!-- Menu choix groupe -->
+    {{form_groups_choice|safe}}
+
+    <div>
+        {{tab_absences_html|safe}}
+    </div>
+  </div>
+
+{% endblock %}
+
+
+{% block scripts %}
+  {{ super() }}
+  <script src="{{scu.STATIC_DIR}}/js/groups_view.js"></script>
+{% endblock %}
diff --git a/app/templates/formsemestre/groups_lists.j2 b/app/templates/formsemestre/groups_lists.j2
new file mode 100644
index 0000000000000000000000000000000000000000..eeb0fdd156d2f0a09a4af9c8f85ab960999b6c94
--- /dev/null
+++ b/app/templates/formsemestre/groups_lists.j2
@@ -0,0 +1,37 @@
+{# Liste des membres d'un ou plusieurs groupes #}
+
+{% extends "sco_page.j2" %}
+
+{% block styles %}
+  {{ super() }}
+  <style>
+    span.warning_unauthorized {
+        color: pink;
+        font-style: italic;
+        margin-left: 12px;
+    }
+  </style>
+{% endblock styles %}
+
+
+
+{% block title %}
+  {{groups_titles}}
+{% endblock title %}
+
+{% block app_content %}
+  <div class="pageContent">
+    <!-- Menu choix groupe -->
+    {{form_groups_choice|safe}}
+
+    <div>
+        {{groups_table|safe}}
+    </div>
+  </div>
+{% endblock %}
+
+
+{% block scripts %}
+  {{ super() }}
+  <script src="{{scu.STATIC_DIR}}/js/groups_view.js"></script>
+{% endblock %}
diff --git a/app/templates/formsemestre/groups_photos.j2 b/app/templates/formsemestre/groups_photos.j2
new file mode 100644
index 0000000000000000000000000000000000000000..68d1516facf3b39372c0558882d30102d2c5caef
--- /dev/null
+++ b/app/templates/formsemestre/groups_photos.j2
@@ -0,0 +1,27 @@
+{# Trombinoscope HTML #}
+
+{% extends "sco_page.j2" %}
+
+
+{% block title %}
+  Photos {{groups_titles}}
+{% endblock title %}
+
+
+{% block app_content %}
+  <div class="pageContent">
+    <!-- Menu choix groupe -->
+    {{form_groups_choice|safe}}
+
+    <div>
+        {{tab_photos_html|safe}}
+    </div>
+  </div>
+
+{% endblock %}
+
+
+{% block scripts %}
+  {{ super() }}
+  <script src="{{scu.STATIC_DIR}}/js/groups_view.js"></script>
+{% endblock %}
diff --git a/app/templates/formsemestre_header.j2 b/app/templates/formsemestre_header.j2
index d8d7693e474438c4faba66ef20ec164cc9d89357..1e5019a9158b8e4d30f96b4135d5e5dd90e9fb58 100644
--- a/app/templates/formsemestre_header.j2
+++ b/app/templates/formsemestre_header.j2
@@ -21,7 +21,7 @@
         </span>
         <span class="resp"><a
                 title="{{sco.formsemestre.responsables_str(abbrev_prenom=False)}}">{{sco.formsemestre.responsables_str()}}</a></span>
-        <span class="nbinscrits"><a class="discretelink" href="{{url_for('scolar.groups_view', scodoc_dept=g.scodoc_dept,
+        <span class="nbinscrits"><a class="discretelink" href="{{url_for('scolar.groups_lists', scodoc_dept=g.scodoc_dept,
         formsemestre_id=sco.formsemestre.id)}}">{{sco.formsemestre.inscriptions|length}} inscrits</a></span>
         <span class="lock">
             {% if not sco.formsemestre.etat %}<a href="{{url_for('notes.formsemestre_flip_lock', scodoc_dept=g.scodoc_dept,
diff --git a/app/templates/formsemestre_page_title.j2 b/app/templates/formsemestre_page_title.j2
index 1d6f4f1f6b15fc93d7e6ec6ae5a49fabd09f37fd..075faf9b13c7f396145b6c5df3fb409cc0c0c50b 100644
--- a/app/templates/formsemestre_page_title.j2
+++ b/app/templates/formsemestre_page_title.j2
@@ -17,7 +17,7 @@
         au {{formsemestre.date_fin.strftime('%d/%m/%Y')}} ">{{formsemestre.mois_debut()}} -
                 {{formsemestre.mois_fin()}}</a></span><span class="resp"><a
                 title="{{formsemestre.responsables_str(abbrev_prenom=False)}}">{{formsemestre.responsables_str()}}</a></span><span
-            class="nbinscrits"><a class="discretelink" href="{{url_for('scolar.groups_view',
+            class="nbinscrits"><a class="discretelink" href="{{url_for('scolar.groups_lists',
             scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
         }}">{{formsemestre.etuds_inscriptions|length}} inscrits</a></span><span class="lock">
             {%-if not formsemestre.etat -%}
diff --git a/app/views/scolar.py b/app/views/scolar.py
index 60e89d4dd741d252f89da9362164d52c1cdcfb0a..b375b533126ae3c2c3a009855c437f90e3d96a4c 100644
--- a/app/views/scolar.py
+++ b/app/views/scolar.py
@@ -461,11 +461,11 @@ sco_publish(
 )
 
 
-@bp.route("/groups_view")
+@bp.route("/groups_lists")
 @scodoc
-@permission_required_compat_scodoc7(Permission.ScoView)
+@permission_required(Permission.ScoView)
 @scodoc7func
-def groups_view(
+def groups_lists(
     group_ids=(),
     fmt="html",
     # Options pour listes:
@@ -477,10 +477,10 @@ def groups_view(
     with_bourse=0,
     formsemestre_id=None,
 ):
-    return sco_groups_view.groups_view(
+    "Listes des étudiants des groupes"
+    return sco_groups_view.groups_lists(
         group_ids=group_ids,
         fmt=fmt,
-        # Options pour listes:
         with_codes=with_codes,
         etat=etat,
         with_paiement=with_paiement,
@@ -491,6 +491,40 @@ def groups_view(
     )
 
 
+@bp.route("/groups_photos")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def groups_photos(
+    group_ids=(),
+    etat=None,
+    formsemestre_id=None,
+):
+    "trombi HTML"
+    return sco_groups_view.groups_photos(
+        group_ids=group_ids,
+        etat=etat,
+        formsemestre_id=formsemestre_id,
+    )
+
+
+@bp.route("/groups_feuilles")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def groups_feuilles(
+    group_ids=(),
+    etat=None,
+    formsemestre_id=None,
+):
+    "Feuilles appel, liens assiduité, etc."
+    return sco_groups_view.groups_feuilles(
+        group_ids=group_ids,
+        etat=etat,
+        formsemestre_id=formsemestre_id,
+    )
+
+
 sco_publish(
     "/export_groups_as_moodle_csv",
     sco_groups_view.export_groups_as_moodle_csv,
diff --git a/tests/unit/test_formsemestre.py b/tests/unit/test_formsemestre.py
index e9677c4f8d231c7ed8cc65ec0f955bd2bf22ec92..928aae833c38c8963fadb2dcbbb444debe09daef 100644
--- a/tests/unit/test_formsemestre.py
+++ b/tests/unit/test_formsemestre.py
@@ -136,7 +136,9 @@ def test_formsemestre_misc_views(test_client):
     ans = sco_formsemestre_inscriptions.formsemestre_inscrits_ailleurs(formsemestre.id)
 
     # ----- MENU GROUPES
-    ans = call_view(scolar.groups_view, formsemestre.id)
+    ans = call_view(scolar.groups_lists, formsemestre.id)
+    ans = call_view(scolar.groups_photos, formsemestre.id)
+    ans = call_view(scolar.groups_feuilles, formsemestre.id)
     ans = call_view(scolar.partition_editor, formsemestre.id)
     ans = sco_groups.edit_partition_form(formsemestre.id)