v3 : modules d'ordre et de titre — l'interface, restes mineurs (L11, C25–C27)

Chaque section du lecteur finit par l'ordre de ses articles (inertie 2) : ses
articles numérotés, son vote déplié, « Proposer un autre ordre » — une liste où
l'on clique un article puis sa place, ses flèches, ou le clavier. Le titre du
document (inertie 4) se vote et se propose sous le titre, « traduction à
revoir » quand il a changé. Les deux sont dans la liste des propositions, le
panier et la chaîne Ğ1 ; un nouvel article se propose dans une section, à sa fin.
La liste du groupe compte les propositions ouvertes des modules.

Restes mineurs : la participation ramenée se dit en deux temps (la mesure, puis
ce que le groupe demande), les lignes « habituelle » des réglages resserrées, le
sélecteur d'inertie se parcourt aux flèches (le cran actuel atteignable, jamais
choisi), la marque « seed a minima » porte le vrai nombre de votes.

Contrôles : C25 (ordre, dont la chaîne et le téléphone), C26 (titre), C27
(participation ramenée) ; C3, C17 (41 modules), C20 mis à jour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-31 05:35:02 +01:00
co-authored by Claude Opus 5.5
parent 3831ddb1bf
commit 785580c78f
31 changed files with 1480 additions and 136 deletions
+2 -1
View File
@@ -149,8 +149,9 @@ pub async fn group(
"SELECT d.id, d.slug, d.title, d.summary,
(SELECT COUNT(*) FROM units u
WHERE u.doc_id = d.id AND u.kind = 'article' AND u.in_force_id IS NOT NULL) AS in_force,
-- Articles in force; open proposals of articles and modules alike (ADR 0009).
(SELECT COUNT(*) FROM proposals p JOIN units u ON u.id = p.unit_id
WHERE u.doc_id = d.id AND u.kind = 'article' AND p.status = 'open') AS open
WHERE u.doc_id = d.id AND p.status = 'open') AS open
FROM documents d WHERE d.group_slug = ? ORDER BY d.created_at",
)
.bind(&slug)
+12 -1
View File
@@ -183,7 +183,7 @@ async fn a_new_base_has_its_title_and_the_order_of_each_section() {
let id = doc["doc"]["id"].as_str().expect("id");
let all = c.ok("GET", &format!("/api/docs/{id}/preview?mode=all"), None).await;
assert_eq!(all["units"].as_array().expect("units").len(), 33);
// The list of the group counts articles only.
// The list of the group: the articles in force; no module has an open proposal yet.
let g = c.ok("GET", "/api/groups/monnaie-libre", None).await;
assert_eq!((g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()), (Some(3), Some(32)));
}
@@ -250,6 +250,17 @@ async fn articles_that_enter_or_leave_a_section_complete_an_order_under_vote() {
.expect("id")
.to_owned();
yvv.vote(&p, "for").await;
// The list of the group counts it among the proposals in progress, never among the articles.
let g = yvv.ok("GET", "/api/groups/essai", None).await;
let before = doc["units"]
.as_array()
.expect("u")
.iter()
.map(|u| u["proposals"].as_array().expect("p").iter().filter(|x| x["status"] == "open").count() as u64);
assert_eq!(
(g["docs"][0]["inForce"].as_u64(), g["docs"][0]["open"].as_u64()),
(Some(3), Some(before.sum::<u64>() + 1))
);
// A new article enters the section: at its end, and at the end of the order under vote.
let created = chiara
.ok(