diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py
index c585223d1ed526a1e3f343530a805d1febdd1cc6..ae9d7e1ca3c334d1fad87911902c393bfa1b6d3d 100644
--- a/app/scodoc/sco_groups.py
+++ b/app/scodoc/sco_groups.py
@@ -1332,17 +1332,6 @@ def groups_auto_repartition(partition: Partition):
         ),
     ]
 
-    H = [
-        f"""<h2>Répartition des groupes de {partition.partition_name}</h2>
-        <p>Semestre {formsemestre.titre_annee()}</p>
-        <p class="help">Les groupes existants seront <b>effacés</b> et remplacés par
-          ceux créés ici. La répartition aléatoire tente d'uniformiser le niveau
-          des groupes (en utilisant la dernière moyenne générale disponible pour
-          chaque étudiant) et de maximiser la mixité de chaque groupe.
-        </p>
-        """,
-    ]
-
     tf = TrivialFormulator(
         request.base_url,
         scu.get_request_args(),
@@ -1354,58 +1343,59 @@ def groups_auto_repartition(partition: Partition):
     )
     if tf[0] == 0:
         return render_template(
-            "sco_page.j2",
-            title="Répartition des groupes",
-            content="\n".join(H) + "\n" + tf[1],
+            "formsemestre/groups_auto_repartition.j2",
+            title="Répartition dans des groupes",
+            form_html=tf[1],
+            partition=partition,
             sco=ScoData(formsemestre=formsemestre),
         )
-    elif tf[0] == -1:
+    if tf[0] == -1:
         return flask.redirect(dest_url)
-    else:
-        # form submission
-        log(f"groups_auto_repartition({partition})")
-        group_names = tf[2]["groupNames"]
-        group_names = sorted({x.strip() for x in group_names.split(",")})
-        # Détruit les groupes existant de cette partition
-        for group in partition.groups:
-            db.session.delete(group)
-        db.session.commit()
-        # Crée les nouveaux groupes
-        groups = []
-        for group_name in group_names:
-            if group_name.strip():
-                groups.append(partition.create_group(group_name))
-        #
-        nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
-        identdict = nt.identdict
-        # build:  { civilite : liste etudids trie par niveau croissant }
-        civilites = {x["civilite"] for x in identdict.values()}
-        listes = {}
+
+    # form submission
+    log(f"groups_auto_repartition({partition})")
+    group_names = tf[2]["groupNames"]
+    group_names = sorted({x.strip() for x in group_names.split(",")})
+    # Détruit les groupes existant de cette partition
+    for group in partition.groups:
+        db.session.delete(group)
+    db.session.commit()
+    # Crée les nouveaux groupes
+    groups = []
+    for group_name in group_names:
+        if group_name.strip():
+            groups.append(partition.create_group(group_name))
+    #
+    nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
+    identdict = nt.identdict
+    # build:  { civilite : liste etudids trie par niveau croissant }
+    civilites = {x["civilite"] for x in identdict.values()}
+    listes = {}
+    for civilite in civilites:
+        listes[civilite] = [
+            (_get_prev_moy(x["etudid"], formsemestre.id), x["etudid"])
+            for x in identdict.values()
+            if x["civilite"] == civilite
+        ]
+        listes[civilite].sort()
+        log("listes[%s] = %s" % (civilite, listes[civilite]))
+    # affect aux groupes:
+    n = len(identdict)
+    igroup = 0
+    nbgroups = len(groups)
+    while n > 0:
+        log(f"n={n}")
         for civilite in civilites:
-            listes[civilite] = [
-                (_get_prev_moy(x["etudid"], formsemestre.id), x["etudid"])
-                for x in identdict.values()
-                if x["civilite"] == civilite
-            ]
-            listes[civilite].sort()
-            log("listes[%s] = %s" % (civilite, listes[civilite]))
-        # affect aux groupes:
-        n = len(identdict)
-        igroup = 0
-        nbgroups = len(groups)
-        while n > 0:
-            log(f"n={n}")
-            for civilite in civilites:
-                log(f"civilite={civilite}")
-                if len(listes[civilite]):
-                    n -= 1
-                    etudid = listes[civilite].pop()[1]
-                    group = groups[igroup]
-                    igroup = (igroup + 1) % nbgroups
-                    log(f"in {etudid} in group {group.id}")
-                    change_etud_group_in_partition(etudid, group)
-                    log(f"{etudid} in group {group.id}")
-        return flask.redirect(dest_url)
+            log(f"civilite={civilite}")
+            if len(listes[civilite]):
+                n -= 1
+                etudid = listes[civilite].pop()[1]
+                group = groups[igroup]
+                igroup = (igroup + 1) % nbgroups
+                log(f"in {etudid} in group {group.id}")
+                change_etud_group_in_partition(etudid, group)
+                log(f"{etudid} in group {group.id}")
+    return flask.redirect(dest_url)
 
 
 def _get_prev_moy(etudid: int, formsemestre_id: int) -> float | str:
diff --git a/app/static/css/partition_editor.css b/app/static/css/partition_editor.css
index 00bbb2483473f0ef460af5230a659d5c82d3d4ae..90e895bea985d1770f23931c01b45084e9f37cfb 100644
--- a/app/static/css/partition_editor.css
+++ b/app/static/css/partition_editor.css
@@ -309,7 +309,7 @@ body.editionActivated .filtres>div>div>div>div {
 	display: none;
 }
 
-#zonePartitions  span.editing a {
+#zonePartitions span.editing a {
 	text-decoration: none;
 }
 
@@ -400,7 +400,7 @@ body.editionActivated .filtres .nonEditable .move {
 /*****************************/
 /* Zone Etudiants           */
 /*****************************/
-#zoneChoix summary{
+#zoneChoix summary {
 	margin: 0 0 16px;
 	cursor: pointer;
 }
@@ -450,6 +450,7 @@ body.editionActivated .filtres .nonEditable .move {
 	margin-bottom: 4px;
 	width: fit-content;
 }
+
 #zoneChoix .autoAffectation .progress {
 	position: absolute;
 	top: 100%;
@@ -524,7 +525,8 @@ body.editionActivated .filtres .nonEditable .move {
 }
 
 #zoneChoix .etudiants .partition>div,
-#zoneChoix .etudiants .partition span {
+#zoneChoix .etudiants .partition span,
+div.partition-name {
 	display: block;
 	padding: 4px 8px;
 	border: 1px solid #aaa;
@@ -541,12 +543,22 @@ body.editionActivated .filtres .nonEditable .move {
 	color: #fff;
 }
 
-#zoneChoix .etudiants .partition>:nth-child(1) {
+#zoneChoix .etudiants .partition>:nth-child(1),
+div.partition-name {
 	background: #09c;
 	border-color: #09c;
 	color: #fff;
 }
 
+div.partition-name {
+	display: inline-block;
+	width: fit-content;
+}
+
+div.partition-name a {
+	color: #fff !important;
+}
+
 section:not(#zonePartitions) .hide {
 	display: none !important;
 }
diff --git a/app/templates/formsemestre/groups_auto_repartition.j2 b/app/templates/formsemestre/groups_auto_repartition.j2
new file mode 100644
index 0000000000000000000000000000000000000000..34a799b9291c58e5e5717126e999bc4c6beb4769
--- /dev/null
+++ b/app/templates/formsemestre/groups_auto_repartition.j2
@@ -0,0 +1,30 @@
+{# Formulaire répartition auto dans les groupes #}
+
+{% extends "sco_page.j2" %}
+
+{% block app_content %}
+
+<h2>Répartition des groupes de {{partition.partition_name}}</h2>
+
+<div class="scobox">
+<div class="scobox-title">Semestre {{sco.formsemestre.titre_annee()}}</div>
+
+<div class="help space-after-24 space-before-24">
+💡Les groupes existants dans cette partition seront <b>effacés</b> et remplacés
+par ceux créés ici. La répartition aléatoire tente d'uniformiser le niveau des
+groupes (en utilisant la dernière moyenne générale disponible pour chaque
+étudiant) et de maximiser la mixité de chaque groupe.
+</div>
+
+{{form_html|safe}}
+
+</div>
+
+<div>
+<a class="stdlink" href="{{
+    url_for('scolar.partition_editor',
+        scodoc_dept=g.scodoc_dept, formsemestre_id=sco.formsemestre.id)
+}}">Retour à l'éditeur de partitions</a>
+</div>
+
+{% endblock %}
diff --git a/app/templates/scolar/partition_editor.j2 b/app/templates/scolar/partition_editor.j2
index 70ad81bb83a5ed2196b9e5cafa1055f90404f9c3..e334cffdc22ec7c9516cce3f76d619b40d28ce3a 100644
--- a/app/templates/scolar/partition_editor.j2
+++ b/app/templates/scolar/partition_editor.j2
@@ -52,7 +52,23 @@
 		<details>
 			<summary>Outils d'affectation</summary>
 			<div class="autoAffectation">
-				<a href="students_groups_auto_assignment?formsemestre_id={{formsemestre.id}}"><svg
+				<svg
+						xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
+						stroke="#0b0b0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
+						<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M13.8 12H3" />
+				</svg> Répartir les étudiants dans les groupes
+				<div id="partitionsARepartir">
+				{# peuplé en js par listeGroupesRepartition()
+				<div class="partition-name"><a href="">TD</a></div>
+				<div class="partition-name"><a href="">groupes de projets</a></div>
+				#}
+				</div>
+			</div>
+			<div class="autoAffectation">
+				<a href="{{
+					url_for('scolar.students_groups_auto_assignment',
+						scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id
+					)}}"><svg
 						xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
 						stroke="#0b0b0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
 						<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M13.8 12H3" />
@@ -134,6 +150,7 @@
 		processDatas(partitions, etudiants);
 		processEvents();
 		listeGroupesAutoaffectation();
+		listeGroupesRepartition();
 
 		document.querySelector("body").classList.add("loaded");
 		document.querySelector('.wait').style.display = "none";
@@ -354,6 +371,19 @@
 
 	}
 
+	function listeGroupesRepartition() { // peuple la liste des partitions pour répartition auto
+		let output = '';
+		document.querySelectorAll('#zonePartitions .filtres>[data-idpartition]').forEach(partition => {
+			if (!partition.classList.contains('nonEditable')) {
+				output += `
+					<div class="partition-name">
+					<a href="groups_auto_repartition/${partition.dataset.idpartition}">${partition.children[0].children[1].innerText}
+					</a></div>`;
+			}
+		});
+		document.querySelector("#partitionsARepartir").innerHTML = output;
+	}
+
 	/******************************/
 	/* Gestionnaire d'événements  */
 	/******************************/
@@ -677,6 +707,7 @@
 					</div>`;
 
 				listeGroupesAutoaffectation();
+				listeGroupesRepartition();
 			})
 			.catch(error => {
 				document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (1).</h2>";
@@ -730,6 +761,7 @@
 				// divGroupe.querySelector(".modif").click();
 
 				listeGroupesAutoaffectation();
+				listeGroupesRepartition();
 			})
 			.catch(error => {
 				document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (4).</h2>";
@@ -811,6 +843,7 @@
 					document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (2).</h2>";
 				}
 				listeGroupesAutoaffectation();
+				listeGroupesRepartition();
 			})
 			.catch(error => {
 				document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (3).</h2>";
@@ -924,6 +957,7 @@
 					document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (6).</h2>";
 				}
 				listeGroupesAutoaffectation();
+				listeGroupesRepartition();
 			})
 	}
 
@@ -1038,6 +1072,7 @@
 					document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (6).</h2>";
 				}
 				listeGroupesAutoaffectation();
+				listeGroupesRepartition();
 			})
 			.catch(error => {
 				document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (7).</h2>";
diff --git a/app/templates/scolar/students_groups_auto_assignment.j2 b/app/templates/scolar/students_groups_auto_assignment.j2
index 84e5914f72fa1abad3f1c167a276977fca3a48a1..a8630aaac599ccb6e6ae7b7e2af988dca7a07ef7 100644
--- a/app/templates/scolar/students_groups_auto_assignment.j2
+++ b/app/templates/scolar/students_groups_auto_assignment.j2
@@ -1,3 +1,7 @@
+{%- extends 'sco_page.j2' -%}
+
+{% block styles %}
+{{super()}}
 <style>
 	.wait {
 		position: fixed;
@@ -212,7 +216,10 @@
 		z-index: 1;
 	}
 </style>
+{% endblock %}
+
 
+{% block app_content %}
 <main class="moitemoite">
 	<div class="wait"></div>
 	<section>
@@ -249,6 +256,11 @@
 
 </main>
 
+{% endblock %}
+
+{% block scripts %}
+{{ super() }}
+
 <script src="{{scu.STATIC_DIR}}/libjs/xlsx-populate-1.21.0.min.js"></script>
 <script>
 	/************************/
@@ -858,3 +870,4 @@
 	}
 
 </script>
+{% endblock %}
\ No newline at end of file
diff --git a/app/views/scolar.py b/app/views/scolar.py
index 424500d24131f1fad6d34f680e56e5368763fa52..28ead66f6205555536bb065e2c1b8e327c6639bc 100644
--- a/app/views/scolar.py
+++ b/app/views/scolar.py
@@ -1003,15 +1003,12 @@ def partition_editor(formsemestre_id: int, edit_partition=False):
 @scodoc7func
 def students_groups_auto_assignment(formsemestre_id: int):
     """Répartition auto des groupes"""
-    formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
-    H = [
-        render_template(
-            "scolar/students_groups_auto_assignment.j2",
-            formsemestre=formsemestre,
-        ),
-    ]
+    formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
     return render_template(
-        "sco_page.j2", title="Répartition des groupes", content="\n".join(H)
+        "scolar/students_groups_auto_assignment.j2",
+        formsemestre=formsemestre,
+        title="Répartition des groupes",
+        sco=ScoData(formsemestre=formsemestre),
     )
 
 
diff --git a/sco_version.py b/sco_version.py
index 67bc67d63ff850669a90ace21af2b702a83a5812..5f1901f3340a4ae79476e2cdd23cc63982ca391f 100644
--- a/sco_version.py
+++ b/sco_version.py
@@ -1,7 +1,7 @@
 # -*- mode: python -*-
 # -*- coding: utf-8 -*-
 
-SCOVERSION = "9.7.13"
+SCOVERSION = "9.7.14"
 
 SCONAME = "ScoDoc"